gitlab.com/Raven-IO/raven-delve@v1.22.4/service/dap/server_test.go (about)

     1  package dap
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"flag"
     7  	"fmt"
     8  	"io"
     9  	"math"
    10  	"math/rand"
    11  	"net"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"reflect"
    16  	"regexp"
    17  	"runtime"
    18  	"strconv"
    19  	"strings"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/google/go-dap"
    24  	"gitlab.com/Raven-IO/raven-delve/pkg/goversion"
    25  	"gitlab.com/Raven-IO/raven-delve/pkg/logflags"
    26  	"gitlab.com/Raven-IO/raven-delve/pkg/proc"
    27  	protest "gitlab.com/Raven-IO/raven-delve/pkg/proc/test"
    28  	"gitlab.com/Raven-IO/raven-delve/service"
    29  	"gitlab.com/Raven-IO/raven-delve/service/api"
    30  	"gitlab.com/Raven-IO/raven-delve/service/dap/daptest"
    31  	"gitlab.com/Raven-IO/raven-delve/service/debugger"
    32  )
    33  
    34  const (
    35  	stopOnEntry bool = true
    36  	hasChildren bool = true
    37  	noChildren  bool = false
    38  )
    39  
    40  const (
    41  	localsScope  = 1000
    42  	globalsScope = 1001
    43  )
    44  
    45  var testBackend string
    46  
    47  func TestMain(m *testing.M) {
    48  	logOutputVal := ""
    49  	if _, isTeamCityTest := os.LookupEnv("TEAMCITY_VERSION"); isTeamCityTest {
    50  		logOutputVal = "debugger,dap"
    51  	}
    52  	var logOutput string
    53  	flag.StringVar(&logOutput, "log-output", logOutputVal, "configures log output")
    54  	flag.Parse()
    55  	logflags.Setup(logOutput != "", logOutput, "")
    56  	protest.DefaultTestBackend(&testBackend)
    57  	os.Exit(protest.RunTestsWithFixtures(m))
    58  }
    59  
    60  // name is for _fixtures/<name>.go
    61  func runTest(t *testing.T, name string, test func(c *daptest.Client, f protest.Fixture)) {
    62  	runTestBuildFlags(t, name, test, protest.AllNonOptimized, false)
    63  }
    64  
    65  // name is for _fixtures/<name>.go
    66  func runTestBuildFlags(t *testing.T, name string, test func(c *daptest.Client, f protest.Fixture), buildFlags protest.BuildFlags, defaultDebugInfoDirs bool) {
    67  	fixture := protest.BuildFixture(name, buildFlags)
    68  
    69  	// Start the DAP server.
    70  	serverStopped := make(chan struct{})
    71  	client := startDAPServerWithClient(t, defaultDebugInfoDirs, serverStopped)
    72  	defer client.Close()
    73  
    74  	test(client, fixture)
    75  	<-serverStopped
    76  }
    77  
    78  func startDAPServerWithClient(t *testing.T, defaultDebugInfoDirs bool, serverStopped chan struct{}) *daptest.Client {
    79  	server, _ := startDAPServer(t, defaultDebugInfoDirs, serverStopped)
    80  	client := daptest.NewClient(server.config.Listener.Addr().String())
    81  	return client
    82  }
    83  
    84  // Starts an empty server and a stripped down config just to establish a client connection.
    85  // To mock a server created by dap.NewServer(config) or serving dap.NewSession(conn, config, debugger)
    86  // set those arg fields manually after the server creation.
    87  func startDAPServer(t *testing.T, defaultDebugInfoDirs bool, serverStopped chan struct{}) (server *Server, forceStop chan struct{}) {
    88  	// Start the DAP server.
    89  	listener, err := net.Listen("tcp", ":0")
    90  	if err != nil {
    91  		t.Fatal(err)
    92  	}
    93  	debugInfoDirs := []string{}
    94  	if defaultDebugInfoDirs {
    95  		debugInfoDirs = []string{"/usr/lib/debug/.build-id"}
    96  	}
    97  	disconnectChan := make(chan struct{})
    98  	server = NewServer(&service.Config{
    99  		Listener:       listener,
   100  		DisconnectChan: disconnectChan,
   101  		Debugger:       debugger.Config{DebugInfoDirectories: debugInfoDirs},
   102  	})
   103  	server.Run()
   104  	// Give server time to start listening for clients
   105  	time.Sleep(100 * time.Millisecond)
   106  
   107  	// Run a goroutine that stops the server when disconnectChan is signaled.
   108  	// This helps us test that certain events cause the server to stop as
   109  	// expected.
   110  	forceStop = make(chan struct{})
   111  	go func() {
   112  		defer func() {
   113  			if serverStopped != nil {
   114  				close(serverStopped)
   115  			}
   116  		}()
   117  		select {
   118  		case <-disconnectChan:
   119  			t.Log("server stop triggered internally")
   120  		case <-forceStop:
   121  			t.Log("server stop triggered externally")
   122  		}
   123  		server.Stop()
   124  	}()
   125  
   126  	return server, forceStop
   127  }
   128  
   129  func verifyServerStopped(t *testing.T, server *Server) {
   130  	t.Helper()
   131  	if server.listener != nil {
   132  		if server.listener.Close() == nil {
   133  			t.Error("server should have closed listener after shutdown")
   134  		}
   135  	}
   136  	verifySessionStopped(t, server.session)
   137  }
   138  
   139  func verifySessionStopped(t *testing.T, session *Session) {
   140  	t.Helper()
   141  	if session == nil {
   142  		return
   143  	}
   144  	if session.conn == nil {
   145  		t.Error("session must always have a set connection")
   146  	}
   147  	verifyConnStopped(t, session.conn)
   148  	if session.debugger != nil {
   149  		t.Error("session should have no pointer to debugger after shutdown")
   150  	}
   151  	if session.binaryToRemove != "" {
   152  		t.Error("session should have no binary to remove after shutdown")
   153  	}
   154  }
   155  
   156  func verifyConnStopped(t *testing.T, conn io.ReadWriteCloser) {
   157  	t.Helper()
   158  	if conn.Close() == nil {
   159  		t.Error("client connection should be closed after shutdown")
   160  	}
   161  }
   162  
   163  func TestStopNoClient(t *testing.T) {
   164  	for name, triggerStop := range map[string]func(s *Server, forceStop chan struct{}){
   165  		"force":        func(s *Server, forceStop chan struct{}) { close(forceStop) },
   166  		"accept error": func(s *Server, forceStop chan struct{}) { s.config.Listener.Close() },
   167  	} {
   168  		t.Run(name, func(t *testing.T) {
   169  			serverStopped := make(chan struct{})
   170  			server, forceStop := startDAPServer(t, false, serverStopped)
   171  			triggerStop(server, forceStop)
   172  			<-serverStopped
   173  			verifyServerStopped(t, server)
   174  		})
   175  	}
   176  }
   177  
   178  func TestStopNoTarget(t *testing.T) {
   179  	for name, triggerStop := range map[string]func(c *daptest.Client, forceStop chan struct{}){
   180  		"force":      func(c *daptest.Client, forceStop chan struct{}) { close(forceStop) },
   181  		"read error": func(c *daptest.Client, forceStop chan struct{}) { c.Close() },
   182  		"disconnect": func(c *daptest.Client, forceStop chan struct{}) { c.DisconnectRequest() },
   183  	} {
   184  		t.Run(name, func(t *testing.T) {
   185  			serverStopped := make(chan struct{})
   186  			server, forceStop := startDAPServer(t, false, serverStopped)
   187  			client := daptest.NewClient(server.config.Listener.Addr().String())
   188  			defer client.Close()
   189  
   190  			client.InitializeRequest()
   191  			client.ExpectInitializeResponseAndCapabilities(t)
   192  			triggerStop(client, forceStop)
   193  			<-serverStopped
   194  			verifyServerStopped(t, server)
   195  		})
   196  	}
   197  }
   198  
   199  func TestStopWithTarget(t *testing.T) {
   200  	for name, triggerStop := range map[string]func(c *daptest.Client, forceStop chan struct{}){
   201  		"force":                  func(c *daptest.Client, forceStop chan struct{}) { close(forceStop) },
   202  		"read error":             func(c *daptest.Client, forceStop chan struct{}) { c.Close() },
   203  		"disconnect before exit": func(c *daptest.Client, forceStop chan struct{}) { c.DisconnectRequest() },
   204  		"disconnect after  exit": func(c *daptest.Client, forceStop chan struct{}) {
   205  			c.ContinueRequest(1)
   206  			c.ExpectContinueResponse(t)
   207  			c.ExpectTerminatedEvent(t)
   208  			c.DisconnectRequest()
   209  		},
   210  	} {
   211  		t.Run(name, func(t *testing.T) {
   212  			serverStopped := make(chan struct{})
   213  			server, forceStop := startDAPServer(t, false, serverStopped)
   214  			client := daptest.NewClient(server.config.Listener.Addr().String())
   215  			defer client.Close()
   216  
   217  			client.InitializeRequest()
   218  			client.ExpectInitializeResponseAndCapabilities(t)
   219  			fixture := protest.BuildFixture("increment", protest.AllNonOptimized)
   220  			client.LaunchRequest("debug", fixture.Source, stopOnEntry)
   221  			client.ExpectInitializedEvent(t)
   222  			client.ExpectLaunchResponse(t)
   223  			triggerStop(client, forceStop)
   224  			<-serverStopped
   225  			verifyServerStopped(t, server)
   226  		})
   227  	}
   228  }
   229  
   230  func TestSessionStop(t *testing.T) {
   231  	verifySessionState := func(t *testing.T, s *Session, binaryToRemoveSet bool, debuggerSet bool, disconnectChanSet bool) {
   232  		t.Helper()
   233  		if binaryToRemoveSet && s.binaryToRemove == "" || !binaryToRemoveSet && s.binaryToRemove != "" {
   234  			t.Errorf("binaryToRemove: got %s, want set=%v", s.binaryToRemove, binaryToRemoveSet)
   235  		}
   236  		if debuggerSet && s.debugger == nil || !debuggerSet && s.debugger != nil {
   237  			t.Errorf("debugger: got %v, want set=%v", s.debugger, debuggerSet)
   238  		}
   239  		if disconnectChanSet && s.config.DisconnectChan == nil || !disconnectChanSet && s.config.DisconnectChan != nil {
   240  			t.Errorf("disconnectChan: got %v, want set=%v", s.config.DisconnectChan, disconnectChanSet)
   241  		}
   242  	}
   243  	for name, stopSession := range map[string]func(s *Session, c *daptest.Client, serveDone chan struct{}){
   244  		"force": func(s *Session, c *daptest.Client, serveDone chan struct{}) {
   245  			s.Close()
   246  			<-serveDone
   247  			verifySessionState(t, s, false /*binaryToRemoveSet*/, false /*debuggerSet*/, false /*disconnectChanSet*/)
   248  		},
   249  		"read error": func(s *Session, c *daptest.Client, serveDone chan struct{}) {
   250  			c.Close()
   251  			<-serveDone
   252  			verifyConnStopped(t, s.conn)
   253  			verifySessionState(t, s, true /*binaryToRemoveSet*/, true /*debuggerSet*/, false /*disconnectChanSet*/)
   254  			s.Close()
   255  		},
   256  		"disconnect before exit": func(s *Session, c *daptest.Client, serveDone chan struct{}) {
   257  			c.DisconnectRequest()
   258  			<-serveDone
   259  			verifyConnStopped(t, s.conn)
   260  			verifySessionState(t, s, true /*binaryToRemoveSet*/, false /*debuggerSet*/, false /*disconnectChanSet*/)
   261  			s.Close()
   262  		},
   263  		"disconnect after exit": func(s *Session, c *daptest.Client, serveDone chan struct{}) {
   264  			c.ContinueRequest(1)
   265  			c.ExpectContinueResponse(t)
   266  			c.ExpectTerminatedEvent(t)
   267  			c.DisconnectRequest()
   268  			<-serveDone
   269  			verifyConnStopped(t, s.conn)
   270  			verifySessionState(t, s, true /*binaryToRemoveSet*/, false /*debuggerSet*/, false /*disconnectChanSet*/)
   271  			s.Close()
   272  		},
   273  	} {
   274  		t.Run(name, func(t *testing.T) {
   275  			listener, err := net.Listen("tcp", ":0")
   276  			if err != nil {
   277  				t.Fatalf("cannot setup listener required for testing: %v", err)
   278  			}
   279  			defer listener.Close()
   280  			acceptDone := make(chan struct{})
   281  			var conn net.Conn
   282  			go func() {
   283  				conn, err = listener.Accept()
   284  				close(acceptDone)
   285  			}()
   286  			time.Sleep(10 * time.Millisecond) // give time to start listening
   287  			client := daptest.NewClient(listener.Addr().String())
   288  			defer client.Close()
   289  			<-acceptDone
   290  			if err != nil {
   291  				t.Fatalf("cannot accept client required for testing: %v", err)
   292  			}
   293  			session := NewSession(conn, &Config{
   294  				Config:        &service.Config{DisconnectChan: make(chan struct{})},
   295  				StopTriggered: make(chan struct{}),
   296  			}, nil)
   297  			serveDAPCodecDone := make(chan struct{})
   298  			go func() {
   299  				session.ServeDAPCodec()
   300  				close(serveDAPCodecDone)
   301  			}()
   302  			time.Sleep(10 * time.Millisecond) // give time to start reading
   303  			client.InitializeRequest()
   304  			client.ExpectInitializeResponseAndCapabilities(t)
   305  			fixture := protest.BuildFixture("increment", protest.AllNonOptimized)
   306  			client.LaunchRequest("debug", fixture.Source, stopOnEntry)
   307  			client.ExpectInitializedEvent(t)
   308  			client.ExpectLaunchResponse(t)
   309  			stopSession(session, client, serveDAPCodecDone)
   310  			verifySessionStopped(t, session)
   311  		})
   312  	}
   313  }
   314  
   315  func TestForceStopWhileStopping(t *testing.T) {
   316  	serverStopped := make(chan struct{})
   317  	server, forceStop := startDAPServer(t, false, serverStopped)
   318  	client := daptest.NewClient(server.config.Listener.Addr().String())
   319  
   320  	client.InitializeRequest()
   321  	client.ExpectInitializeResponseAndCapabilities(t)
   322  	fixture := protest.BuildFixture("increment", protest.AllNonOptimized)
   323  	client.LaunchRequest("exec", fixture.Path, stopOnEntry)
   324  	client.ExpectInitializedEvent(t)
   325  	client.Close() // depending on timing may trigger Stop()
   326  	time.Sleep(time.Microsecond)
   327  	close(forceStop) // depending on timing may trigger Stop()
   328  	<-serverStopped
   329  	verifyServerStopped(t, server)
   330  }
   331  
   332  // TestLaunchStopOnEntry emulates the message exchange that can be observed with
   333  // VS Code for the most basic launch debug session with "stopOnEntry" enabled:
   334  //
   335  //	User selects "Start Debugging":  1 >> initialize
   336  //	                              :  1 << initialize
   337  //	                              :  2 >> launch
   338  //	                              :    << initialized event
   339  //	                              :  2 << launch
   340  //	                              :  3 >> setBreakpoints (empty)
   341  //	                              :  3 << setBreakpoints
   342  //	                              :  4 >> setExceptionBreakpoints (empty)
   343  //	                              :  4 << setExceptionBreakpoints
   344  //	                              :  5 >> configurationDone
   345  //	Program stops upon launching  :    << stopped event
   346  //	                              :  5 << configurationDone
   347  //	                              :  6 >> threads
   348  //	                              :  6 << threads (Dummy)
   349  //	                              :  7 >> threads
   350  //	                              :  7 << threads (Dummy)
   351  //	                              :  8 >> stackTrace
   352  //	                              :  8 << error (Unable to produce stack trace)
   353  //	                              :  9 >> stackTrace
   354  //	                              :  9 << error (Unable to produce stack trace)
   355  //	User evaluates bad expression : 10 >> evaluate
   356  //	                              : 10 << error (unable to find function context)
   357  //	User evaluates good expression: 11 >> evaluate
   358  //	                              : 11 << evaluate
   359  //	User selects "Continue"       : 12 >> continue
   360  //	                              : 12 << continue
   361  //	Program runs to completion    :    << terminated event
   362  //	                              : 13 >> disconnect
   363  //	                              :    << output event (Process exited)
   364  //	                              :    << output event (Detaching)
   365  //	                              : 13 << disconnect
   366  //
   367  // This test exhaustively tests Seq and RequestSeq on all messages from the
   368  // server. Other tests do not necessarily need to repeat all these checks.
   369  func TestLaunchStopOnEntry(t *testing.T) {
   370  	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
   371  		// 1 >> initialize, << initialize
   372  		client.InitializeRequest()
   373  		initResp := client.ExpectInitializeResponseAndCapabilities(t)
   374  		if initResp.Seq != 0 || initResp.RequestSeq != 1 {
   375  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=1", initResp)
   376  		}
   377  
   378  		// 2 >> launch, << initialized, << launch
   379  		client.LaunchRequest("exec", fixture.Path, stopOnEntry)
   380  		initEvent := client.ExpectInitializedEvent(t)
   381  		if initEvent.Seq != 0 {
   382  			t.Errorf("\ngot %#v\nwant Seq=0", initEvent)
   383  		}
   384  		launchResp := client.ExpectLaunchResponse(t)
   385  		if launchResp.Seq != 0 || launchResp.RequestSeq != 2 {
   386  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=2", launchResp)
   387  		}
   388  
   389  		// 3 >> setBreakpoints, << setBreakpoints
   390  		client.SetBreakpointsRequest(fixture.Source, nil)
   391  		sbpResp := client.ExpectSetBreakpointsResponse(t)
   392  		if sbpResp.Seq != 0 || sbpResp.RequestSeq != 3 || len(sbpResp.Body.Breakpoints) != 0 {
   393  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=3, len(Breakpoints)=0", sbpResp)
   394  		}
   395  
   396  		// 4 >> setExceptionBreakpoints, << setExceptionBreakpoints
   397  		client.SetExceptionBreakpointsRequest()
   398  		sebpResp := client.ExpectSetExceptionBreakpointsResponse(t)
   399  		if sebpResp.Seq != 0 || sebpResp.RequestSeq != 4 {
   400  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=4", sebpResp)
   401  		}
   402  
   403  		// 5 >> configurationDone, << stopped, << configurationDone
   404  		client.ConfigurationDoneRequest()
   405  		stopEvent := client.ExpectStoppedEvent(t)
   406  		if stopEvent.Seq != 0 ||
   407  			stopEvent.Body.Reason != "entry" ||
   408  			stopEvent.Body.ThreadId != 1 ||
   409  			!stopEvent.Body.AllThreadsStopped {
   410  			t.Errorf("\ngot %#v\nwant Seq=0, Body={Reason=\"entry\", ThreadId=1, AllThreadsStopped=true}", stopEvent)
   411  		}
   412  		cdResp := client.ExpectConfigurationDoneResponse(t)
   413  		if cdResp.Seq != 0 || cdResp.RequestSeq != 5 {
   414  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=5", cdResp)
   415  		}
   416  
   417  		// 6 >> threads, << threads
   418  		client.ThreadsRequest()
   419  		tResp := client.ExpectThreadsResponse(t)
   420  		if tResp.Seq != 0 || tResp.RequestSeq != 6 || len(tResp.Body.Threads) != 1 {
   421  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=6 len(Threads)=1", tResp)
   422  		}
   423  		if tResp.Body.Threads[0].Id != 1 || tResp.Body.Threads[0].Name != "Dummy" {
   424  			t.Errorf("\ngot %#v\nwant Id=1, Name=\"Dummy\"", tResp)
   425  		}
   426  
   427  		// 7 >> threads, << threads
   428  		client.ThreadsRequest()
   429  		tResp = client.ExpectThreadsResponse(t)
   430  		if tResp.Seq != 0 || tResp.RequestSeq != 7 || len(tResp.Body.Threads) != 1 {
   431  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=7 len(Threads)=1", tResp)
   432  		}
   433  
   434  		// 8 >> stackTrace, << error
   435  		client.StackTraceRequest(1, 0, 20)
   436  		stResp := client.ExpectInvisibleErrorResponse(t)
   437  		if stResp.Seq != 0 || stResp.RequestSeq != 8 || !checkErrorMessageFormat(stResp.Body.Error, "Unable to produce stack trace: unknown goroutine 1") {
   438  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=8 Format=\"Unable to produce stack trace: unknown goroutine 1\"", stResp)
   439  		}
   440  
   441  		// 9 >> stackTrace, << error
   442  		client.StackTraceRequest(1, 0, 20)
   443  		stResp = client.ExpectInvisibleErrorResponse(t)
   444  		if stResp.Seq != 0 || stResp.RequestSeq != 9 || !checkErrorMessageId(stResp.Body.Error, UnableToProduceStackTrace) {
   445  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=9 Id=%d", stResp, UnableToProduceStackTrace)
   446  		}
   447  
   448  		// 10 >> evaluate, << error
   449  		client.EvaluateRequest("foo", 0 /*no frame specified*/, "repl")
   450  		erResp := client.ExpectInvisibleErrorResponse(t)
   451  		if erResp.Seq != 0 || erResp.RequestSeq != 10 || !checkErrorMessageId(erResp.Body.Error, UnableToEvaluateExpression) {
   452  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=10 Id=%d", erResp, UnableToEvaluateExpression)
   453  		}
   454  
   455  		// 11 >> evaluate, << evaluate
   456  		client.EvaluateRequest("1+1", 0 /*no frame specified*/, "repl")
   457  		evResp := client.ExpectEvaluateResponse(t)
   458  		if evResp.Seq != 0 || evResp.RequestSeq != 11 || evResp.Body.Result != "2" {
   459  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=10 Result=2", evResp)
   460  		}
   461  
   462  		// 12 >> continue, << continue, << terminated
   463  		client.ContinueRequest(1)
   464  		contResp := client.ExpectContinueResponse(t)
   465  		if contResp.Seq != 0 || contResp.RequestSeq != 12 || !contResp.Body.AllThreadsContinued {
   466  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=12 Body.AllThreadsContinued=true", contResp)
   467  		}
   468  		termEvent := client.ExpectTerminatedEvent(t)
   469  		if termEvent.Seq != 0 {
   470  			t.Errorf("\ngot %#v\nwant Seq=0", termEvent)
   471  		}
   472  
   473  		// 13 >> disconnect, << disconnect
   474  		client.DisconnectRequest()
   475  		oep := client.ExpectOutputEventProcessExited(t, 0)
   476  		if oep.Seq != 0 || oep.Body.Category != "console" {
   477  			t.Errorf("\ngot %#v\nwant Seq=0 Category='console'", oep)
   478  		}
   479  		oed := client.ExpectOutputEventDetaching(t)
   480  		if oed.Seq != 0 || oed.Body.Category != "console" {
   481  			t.Errorf("\ngot %#v\nwant Seq=0 Category='console'", oed)
   482  		}
   483  		dResp := client.ExpectDisconnectResponse(t)
   484  		if dResp.Seq != 0 || dResp.RequestSeq != 13 {
   485  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=13", dResp)
   486  		}
   487  		client.ExpectTerminatedEvent(t)
   488  	})
   489  }
   490  
   491  // TestAttachStopOnEntry is like TestLaunchStopOnEntry, but with attach request.
   492  func TestAttachStopOnEntry(t *testing.T) {
   493  	runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
   494  		// Start the program to attach to
   495  		cmd := exec.Command(fixture.Path)
   496  		stdout, err := cmd.StdoutPipe()
   497  		if err != nil {
   498  			t.Fatal(err)
   499  		}
   500  		cmd.Stderr = os.Stderr
   501  		if err := cmd.Start(); err != nil {
   502  			t.Fatal(err)
   503  		}
   504  		// Wait for output.
   505  		// This will give the target process time to initialize the runtime before we attach,
   506  		// so we can rely on having goroutines when they are requested on attach.
   507  		scanOut := bufio.NewScanner(stdout)
   508  		scanOut.Scan()
   509  		if scanOut.Text() != "past main" {
   510  			t.Errorf("expected loopprog.go to output \"past main\"")
   511  		}
   512  
   513  		// 1 >> initialize, << initialize
   514  		client.InitializeRequest()
   515  		initResp := client.ExpectInitializeResponseAndCapabilities(t)
   516  		if initResp.Seq != 0 || initResp.RequestSeq != 1 {
   517  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=1", initResp)
   518  		}
   519  
   520  		// 2 >> attach, << initialized, << attach
   521  		client.AttachRequest(
   522  			map[string]interface{}{"mode": "local", "processId": cmd.Process.Pid, "stopOnEntry": true, "backend": "default"})
   523  		client.ExpectCapabilitiesEventSupportTerminateDebuggee(t)
   524  		initEvent := client.ExpectInitializedEvent(t)
   525  		if initEvent.Seq != 0 {
   526  			t.Errorf("\ngot %#v\nwant Seq=0", initEvent)
   527  		}
   528  		attachResp := client.ExpectAttachResponse(t)
   529  		if attachResp.Seq != 0 || attachResp.RequestSeq != 2 {
   530  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=2", attachResp)
   531  		}
   532  
   533  		// 3 >> setBreakpoints, << setBreakpoints
   534  		client.SetBreakpointsRequest(fixture.Source, nil)
   535  		sbpResp := client.ExpectSetBreakpointsResponse(t)
   536  		if sbpResp.Seq != 0 || sbpResp.RequestSeq != 3 || len(sbpResp.Body.Breakpoints) != 0 {
   537  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=3, len(Breakpoints)=0", sbpResp)
   538  		}
   539  
   540  		// 4 >> setExceptionBreakpoints, << setExceptionBreakpoints
   541  		client.SetExceptionBreakpointsRequest()
   542  		sebpResp := client.ExpectSetExceptionBreakpointsResponse(t)
   543  		if sebpResp.Seq != 0 || sebpResp.RequestSeq != 4 {
   544  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=4", sebpResp)
   545  		}
   546  
   547  		// 5 >> configurationDone, << stopped, << configurationDone
   548  		client.ConfigurationDoneRequest()
   549  		stopEvent := client.ExpectStoppedEvent(t)
   550  		if stopEvent.Seq != 0 ||
   551  			stopEvent.Body.Reason != "entry" ||
   552  			stopEvent.Body.ThreadId != 1 ||
   553  			!stopEvent.Body.AllThreadsStopped {
   554  			t.Errorf("\ngot %#v\nwant Seq=0, Body={Reason=\"entry\", ThreadId=1, AllThreadsStopped=true}", stopEvent)
   555  		}
   556  		cdResp := client.ExpectConfigurationDoneResponse(t)
   557  		if cdResp.Seq != 0 || cdResp.RequestSeq != 5 {
   558  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=5", cdResp)
   559  		}
   560  
   561  		// 6 >> threads, << threads
   562  		client.ThreadsRequest()
   563  		tResp := client.ExpectThreadsResponse(t)
   564  		// Expect main goroutine plus runtime at this point.
   565  		if tResp.Seq != 0 || tResp.RequestSeq != 6 || len(tResp.Body.Threads) < 2 {
   566  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=6 len(Threads)>1", tResp)
   567  		}
   568  
   569  		// 7 >> threads, << threads
   570  		client.ThreadsRequest()
   571  		client.ExpectThreadsResponse(t)
   572  
   573  		// 8 >> stackTrace, << response
   574  		client.StackTraceRequest(1, 0, 20)
   575  		client.ExpectStackTraceResponse(t)
   576  
   577  		// 9 >> stackTrace, << response
   578  		client.StackTraceRequest(1, 0, 20)
   579  		client.ExpectStackTraceResponse(t)
   580  
   581  		// 10 >> evaluate, << error
   582  		client.EvaluateRequest("foo", 0 /*no frame specified*/, "repl")
   583  		erResp := client.ExpectInvisibleErrorResponse(t)
   584  		if erResp.Seq != 0 || erResp.RequestSeq != 10 || !checkErrorMessageId(erResp.Body.Error, UnableToEvaluateExpression) {
   585  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=10 Id=%d", erResp, UnableToEvaluateExpression)
   586  		}
   587  
   588  		// 11 >> evaluate, << evaluate
   589  		client.EvaluateRequest("1+1", 0 /*no frame specified*/, "repl")
   590  		evResp := client.ExpectEvaluateResponse(t)
   591  		if evResp.Seq != 0 || evResp.RequestSeq != 11 || evResp.Body.Result != "2" {
   592  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=10 Result=2", evResp)
   593  		}
   594  
   595  		// 12 >> continue, << continue
   596  		client.ContinueRequest(1)
   597  		cResp := client.ExpectContinueResponse(t)
   598  		if cResp.Seq != 0 || cResp.RequestSeq != 12 {
   599  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=12", cResp)
   600  		}
   601  
   602  		// TODO(polina): once https://gitlab.com/Raven-IO/raven-delve/issues/2259 is
   603  		// fixed, test with kill=false.
   604  
   605  		// 13 >> disconnect, << disconnect
   606  		client.DisconnectRequestWithKillOption(true)
   607  
   608  		// Disconnect consists of Halt + Detach.
   609  		// Halt interrupts command in progress, which triggers
   610  		// a stopped event in parallel with the disconnect
   611  		// sequence. It might arrive before or during the sequence
   612  		// or never if the server exits before it is sent.
   613  		msg := expectMessageFilterStopped(t, client)
   614  		client.CheckOutputEvent(t, msg)
   615  		msg = expectMessageFilterStopped(t, client)
   616  		client.CheckDisconnectResponse(t, msg)
   617  		client.ExpectTerminatedEvent(t)
   618  
   619  		// If this call to KeepAlive isn't here there's a chance that stdout will
   620  		// be garbage collected (since it is no longer alive long before this
   621  		// point), when that happens, on unix-like OSes, the read end of the pipe
   622  		// will be closed by the finalizer and the target process will die by
   623  		// SIGPIPE, which the rest of this test does not expect.
   624  		runtime.KeepAlive(stdout)
   625  	})
   626  }
   627  
   628  // Like the test above, except the program is configured to continue on entry.
   629  func TestContinueOnEntry(t *testing.T) {
   630  	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
   631  		// 1 >> initialize, << initialize
   632  		client.InitializeRequest()
   633  		client.ExpectInitializeResponseAndCapabilities(t)
   634  
   635  		// 2 >> launch, << initialized, << launch
   636  		client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
   637  		client.ExpectInitializedEvent(t)
   638  		client.ExpectLaunchResponse(t)
   639  
   640  		// 3 >> setBreakpoints, << setBreakpoints
   641  		client.SetBreakpointsRequest(fixture.Source, nil)
   642  		client.ExpectSetBreakpointsResponse(t)
   643  
   644  		// 4 >> setExceptionBreakpoints, << setExceptionBreakpoints
   645  		client.SetExceptionBreakpointsRequest()
   646  		client.ExpectSetExceptionBreakpointsResponse(t)
   647  
   648  		// 5 >> configurationDone, << configurationDone
   649  		client.ConfigurationDoneRequest()
   650  		client.ExpectConfigurationDoneResponse(t)
   651  		// "Continue" happens behind the scenes on another goroutine
   652  
   653  		client.ExpectTerminatedEvent(t)
   654  
   655  		// 6 >> threads, << threads
   656  		client.ThreadsRequest()
   657  		tResp := client.ExpectThreadsResponse(t)
   658  		if tResp.Seq != 0 || tResp.RequestSeq != 6 || len(tResp.Body.Threads) != 1 {
   659  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=6 len(Threads)=1", tResp)
   660  		}
   661  		if tResp.Body.Threads[0].Id != 1 || tResp.Body.Threads[0].Name != "Dummy" {
   662  			t.Errorf("\ngot %#v\nwant Id=1, Name=\"Dummy\"", tResp)
   663  		}
   664  
   665  		// 7 >> disconnect, << disconnect
   666  		client.DisconnectRequest()
   667  		client.ExpectOutputEventProcessExited(t, 0)
   668  		client.ExpectOutputEventDetaching(t)
   669  		dResp := client.ExpectDisconnectResponse(t)
   670  		if dResp.Seq != 0 || dResp.RequestSeq != 7 {
   671  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=7", dResp)
   672  		}
   673  		client.ExpectTerminatedEvent(t)
   674  	})
   675  }
   676  
   677  // TestPreSetBreakpoint corresponds to a debug session that is configured to
   678  // continue on entry with a pre-set breakpoint.
   679  func TestPreSetBreakpoint(t *testing.T) {
   680  	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
   681  		client.InitializeRequest()
   682  		client.ExpectInitializeResponseAndCapabilities(t)
   683  
   684  		client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
   685  		client.ExpectInitializedEvent(t)
   686  		client.ExpectLaunchResponse(t)
   687  
   688  		client.SetBreakpointsRequest(fixture.Source, []int{8})
   689  		sResp := client.ExpectSetBreakpointsResponse(t)
   690  		if len(sResp.Body.Breakpoints) != 1 {
   691  			t.Errorf("got %#v, want len(Breakpoints)=1", sResp)
   692  		}
   693  		bkpt0 := sResp.Body.Breakpoints[0]
   694  		if !bkpt0.Verified || bkpt0.Line != 8 || bkpt0.Id != 1 || bkpt0.Source.Name != filepath.Base(fixture.Source) || bkpt0.Source.Path != fixture.Source {
   695  			t.Errorf("got breakpoints[0] = %#v, want Verified=true, Line=8, Id=1, Path=%q", bkpt0, fixture.Source)
   696  		}
   697  
   698  		client.SetExceptionBreakpointsRequest()
   699  		client.ExpectSetExceptionBreakpointsResponse(t)
   700  
   701  		client.ConfigurationDoneRequest()
   702  		client.ExpectConfigurationDoneResponse(t)
   703  		// This triggers "continue" on a separate goroutine
   704  
   705  		client.ThreadsRequest()
   706  		// Since we are in async mode while running, we might receive messages in either order.
   707  		for i := 0; i < 2; i++ {
   708  			msg := client.ExpectMessage(t)
   709  			switch m := msg.(type) {
   710  			case *dap.ThreadsResponse:
   711  				// If the thread request arrived while the program was running, we expect to get the dummy response
   712  				// with a single goroutine "Current".
   713  				// If the thread request arrived after the stop, we should get the goroutine stopped at main.Increment.
   714  				if (len(m.Body.Threads) != 1 || m.Body.Threads[0].Id != -1 || m.Body.Threads[0].Name != "Current") &&
   715  					(len(m.Body.Threads) < 1 || m.Body.Threads[0].Id != 1 || !strings.HasPrefix(m.Body.Threads[0].Name, "* [Go 1] main.Increment")) {
   716  					t.Errorf("\ngot  %#v\nwant Id=-1, Name=\"Current\" or Id=1, Name=\"* [Go 1] main.Increment ...\"", m.Body.Threads)
   717  				}
   718  			case *dap.StoppedEvent:
   719  				if m.Body.Reason != "breakpoint" || m.Body.ThreadId != 1 || !m.Body.AllThreadsStopped {
   720  					t.Errorf("got %#v, want Body={Reason=\"breakpoint\", ThreadId=1, AllThreadsStopped=true}", m)
   721  				}
   722  			default:
   723  				t.Fatalf("got %#v, want ThreadsResponse or StoppedEvent", m)
   724  			}
   725  		}
   726  
   727  		// Threads-StackTrace-Scopes-Variables request waterfall is
   728  		// triggered on stop event.
   729  		client.ThreadsRequest()
   730  		tResp := client.ExpectThreadsResponse(t)
   731  		if len(tResp.Body.Threads) < 2 { // 1 main + runtime
   732  			t.Errorf("\ngot  %#v\nwant len(Threads)>1", tResp.Body.Threads)
   733  		}
   734  		reMain := regexp.MustCompile(`\* \[Go 1\] main.Increment \(Thread [0-9]+\)`)
   735  		wantMain := dap.Thread{Id: 1, Name: "* [Go 1] main.Increment (Thread ...)"}
   736  		wantRuntime := dap.Thread{Id: 2, Name: "[Go 2] runtime.gopark"}
   737  		for _, got := range tResp.Body.Threads {
   738  			if got.Id != 1 && !reMain.MatchString(got.Name) && !(strings.Contains(got.Name, "runtime.") || strings.Contains(got.Name, "runtime/")) {
   739  				t.Errorf("\ngot  %#v\nwant []dap.Thread{%#v, %#v, ...}", tResp.Body.Threads, wantMain, wantRuntime)
   740  			}
   741  		}
   742  
   743  		client.StackTraceRequest(1, 0, 20)
   744  		stResp := client.ExpectStackTraceResponse(t)
   745  
   746  		if stResp.Body.TotalFrames != 6 {
   747  			t.Errorf("\ngot %#v\nwant TotalFrames=6", stResp.Body.TotalFrames)
   748  		}
   749  		if len(stResp.Body.StackFrames) != 6 {
   750  			t.Errorf("\ngot %#v\nwant len(StackFrames)=6", stResp.Body.StackFrames)
   751  		} else {
   752  			checkFrame := func(got dap.StackFrame, id int, name string, sourceName string, line int) {
   753  				t.Helper()
   754  				if got.Id != id || got.Name != name {
   755  					t.Errorf("\ngot  %#v\nwant Id=%d Name=%s", got, id, name)
   756  				}
   757  				if (sourceName != "" && (got.Source == nil || got.Source.Name != sourceName)) || (line > 0 && got.Line != line) {
   758  					t.Errorf("\ngot  %#v\nwant Source.Name=%s Line=%d", got, sourceName, line)
   759  				}
   760  			}
   761  			checkFrame(stResp.Body.StackFrames[0], 1000, "main.Increment", "increment.go", 8)
   762  			checkFrame(stResp.Body.StackFrames[1], 1001, "main.Increment", "increment.go", 11)
   763  			checkFrame(stResp.Body.StackFrames[2], 1002, "main.Increment", "increment.go", 11)
   764  			checkFrame(stResp.Body.StackFrames[3], 1003, "main.main", "increment.go", 17)
   765  			checkFrame(stResp.Body.StackFrames[4], 1004, "runtime.main", "proc.go", -1)
   766  			checkFrame(stResp.Body.StackFrames[5], 1005, "runtime.goexit", "", -1)
   767  		}
   768  
   769  		client.ScopesRequest(1000)
   770  		scopes := client.ExpectScopesResponse(t)
   771  		if len(scopes.Body.Scopes) > 1 {
   772  			t.Errorf("\ngot  %#v\nwant len(Scopes)=1 (Locals)", scopes)
   773  		}
   774  		checkScope(t, scopes, 0, "Locals", localsScope)
   775  
   776  		client.VariablesRequest(localsScope)
   777  		args := client.ExpectVariablesResponse(t)
   778  		checkChildren(t, args, "Locals", 2)
   779  		checkVarExact(t, args, 0, "y", "y", "0 = 0x0", "uint", noChildren)
   780  		checkVarExact(t, args, 1, "~r1", "", "0 = 0x0", "uint", noChildren)
   781  
   782  		client.ContinueRequest(1)
   783  		ctResp := client.ExpectContinueResponse(t)
   784  		if !ctResp.Body.AllThreadsContinued {
   785  			t.Errorf("\ngot  %#v\nwant AllThreadsContinued=true", ctResp.Body)
   786  		}
   787  		// "Continue" is triggered after the response is sent
   788  
   789  		client.ExpectTerminatedEvent(t)
   790  
   791  		// Pause request after termination should result in an error.
   792  		// But in certain cases this request actually succeeds.
   793  		client.PauseRequest(1)
   794  		switch r := client.ExpectMessage(t).(type) {
   795  		case *dap.ErrorResponse:
   796  			if r.Message != "Unable to halt execution" {
   797  				t.Errorf("\ngot  %#v\nwant Message='Unable to halt execution'", r)
   798  			}
   799  		case *dap.PauseResponse:
   800  		default:
   801  			t.Fatalf("Unexpected response type: expect error or pause, got %#v", r)
   802  		}
   803  
   804  		client.DisconnectRequest()
   805  		client.ExpectOutputEventProcessExited(t, 0)
   806  		client.ExpectOutputEventDetaching(t)
   807  		client.ExpectDisconnectResponse(t)
   808  		client.ExpectTerminatedEvent(t)
   809  	})
   810  }
   811  
   812  // checkStackFramesExact is a helper for verifying the values within StackTraceResponse.
   813  //
   814  //	wantStartName - name of the first returned frame (ignored if "")
   815  //	wantStartLine - file line of the first returned frame (ignored if <0).
   816  //	wantStartID - id of the first frame returned (ignored if wantFrames is 0).
   817  //	wantFrames - number of frames returned (length of StackTraceResponse.Body.StackFrames array).
   818  //	wantTotalFrames - total number of stack frames available (StackTraceResponse.Body.TotalFrames).
   819  func checkStackFramesExact(t *testing.T, got *dap.StackTraceResponse,
   820  	wantStartName string, wantStartLine interface{}, wantStartID, wantFrames, wantTotalFrames int,
   821  ) {
   822  	t.Helper()
   823  	checkStackFramesNamed("", t, got, wantStartName, wantStartLine, wantStartID, wantFrames, wantTotalFrames, true)
   824  }
   825  
   826  func TestFilterGoroutines(t *testing.T) {
   827  	tt := []struct {
   828  		name    string
   829  		filter  string
   830  		want    []string
   831  		wantLen int
   832  		wantErr bool
   833  	}{
   834  		{
   835  			name:    "user goroutines",
   836  			filter:  "-with user",
   837  			want:    []string{"main.main", "main.agoroutine"},
   838  			wantLen: 11,
   839  		},
   840  		{
   841  			name:    "filter by user loc",
   842  			filter:  "-with userloc main.main",
   843  			want:    []string{"main.main"},
   844  			wantLen: 1,
   845  		},
   846  		{
   847  			name:    "multiple filters",
   848  			filter:  "-with user -with userloc main.agoroutine",
   849  			want:    []string{"main.agoroutine"},
   850  			wantLen: 10,
   851  		},
   852  		{
   853  			name:   "system goroutines",
   854  			filter: "-without user",
   855  			want:   []string{"runtime."},
   856  		},
   857  		// Filters that should return all goroutines.
   858  		{
   859  			name:    "empty filter string",
   860  			filter:  "",
   861  			want:    []string{"main.main", "main.agoroutine", "runtime."},
   862  			wantLen: -1,
   863  		},
   864  		{
   865  			name:    "bad filter string",
   866  			filter:  "not parsable to filters",
   867  			want:    []string{"main.main", "main.agoroutine", "runtime."},
   868  			wantLen: -1,
   869  			wantErr: true,
   870  		},
   871  		// Filters that should produce none.
   872  		{
   873  			name:    "no match to user loc",
   874  			filter:  "-with userloc main.NotAUserFrame",
   875  			want:    []string{"Dummy"},
   876  			wantLen: 1,
   877  		},
   878  		{
   879  			name:    "no match to user and not user",
   880  			filter:  "-with user -without user",
   881  			want:    []string{"Dummy"},
   882  			wantLen: 1,
   883  		},
   884  	}
   885  	runTest(t, "goroutinestackprog", func(client *daptest.Client, fixture protest.Fixture) {
   886  		runDebugSessionWithBPs(t, client, "launch",
   887  			// Launch
   888  			func() {
   889  				client.LaunchRequestWithArgs(map[string]interface{}{
   890  					"mode":        "exec",
   891  					"program":     fixture.Path,
   892  					"stopOnEntry": !stopOnEntry,
   893  				})
   894  			},
   895  			// Set breakpoints
   896  			fixture.Source, []int{30},
   897  			[]onBreakpoint{{
   898  				// Stop at line 30
   899  				execute: func() {
   900  					for _, tc := range tt {
   901  						command := fmt.Sprintf("dlv config goroutineFilters %s", tc.filter)
   902  						client.EvaluateRequest(command, 1000, "repl")
   903  						client.ExpectInvalidatedEvent(t)
   904  						client.ExpectEvaluateResponse(t)
   905  
   906  						client.ThreadsRequest()
   907  						if tc.wantErr {
   908  							client.ExpectOutputEvent(t)
   909  						}
   910  						tr := client.ExpectThreadsResponse(t)
   911  						if tc.wantLen > 0 && len(tr.Body.Threads) != tc.wantLen {
   912  							t.Errorf("got Threads=%#v, want Len=%d\n", tr.Body.Threads, tc.wantLen)
   913  						}
   914  						for i, frame := range tr.Body.Threads {
   915  							var found bool
   916  							for _, wantName := range tc.want {
   917  								if strings.Contains(frame.Name, wantName) {
   918  									found = true
   919  									break
   920  								}
   921  							}
   922  							if !found {
   923  								t.Errorf("got Threads[%d]=%#v, want Name=%v\n", i, frame, tc.want)
   924  							}
   925  						}
   926  					}
   927  				},
   928  				disconnect: false,
   929  			}})
   930  	})
   931  }
   932  
   933  func checkStackFramesHasMore(t *testing.T, got *dap.StackTraceResponse,
   934  	wantStartName string, wantStartLine, wantStartID, wantFrames, wantTotalFrames int,
   935  ) {
   936  	t.Helper()
   937  	checkStackFramesNamed("", t, got, wantStartName, wantStartLine, wantStartID, wantFrames, wantTotalFrames, false)
   938  }
   939  
   940  func checkStackFramesNamed(testName string, t *testing.T, got *dap.StackTraceResponse,
   941  	wantStartName string, wantStartLine interface{}, wantStartID, wantFrames, wantTotalFrames int, totalExact bool,
   942  ) {
   943  	t.Helper()
   944  	if totalExact && got.Body.TotalFrames != wantTotalFrames {
   945  		t.Errorf("%s\ngot  %#v\nwant TotalFrames=%d", testName, got.Body.TotalFrames, wantTotalFrames)
   946  	} else if !totalExact && got.Body.TotalFrames < wantTotalFrames {
   947  		t.Errorf("%s\ngot  %#v\nwant TotalFrames>=%d", testName, got.Body.TotalFrames, wantTotalFrames)
   948  	}
   949  
   950  	if len(got.Body.StackFrames) != wantFrames {
   951  		t.Errorf("%s\ngot  len(StackFrames)=%d\nwant %d", testName, len(got.Body.StackFrames), wantFrames)
   952  	} else {
   953  		// Verify that frame ids are consecutive numbers starting at wantStartID
   954  		for i := 0; i < wantFrames; i++ {
   955  			if got.Body.StackFrames[i].Id != wantStartID+i {
   956  				t.Errorf("%s\ngot  %#v\nwant Id=%d", testName, got.Body.StackFrames[i], wantStartID+i)
   957  			}
   958  		}
   959  		// Verify the name and line corresponding to the first returned frame (if any).
   960  		// This is useful when the first frame is the frame corresponding to the breakpoint at
   961  		// a predefined line.
   962  
   963  		startLineOk := true
   964  
   965  		switch wantStartLine := wantStartLine.(type) {
   966  		case int:
   967  			if wantStartLine > 0 {
   968  				startLineOk = got.Body.StackFrames[0].Line == wantStartLine
   969  			}
   970  		case []int:
   971  			startLineOk = false
   972  			for _, ln := range wantStartLine {
   973  				if got.Body.StackFrames[0].Line == ln {
   974  					startLineOk = true
   975  					break
   976  				}
   977  			}
   978  		}
   979  
   980  		if wantFrames > 0 && !startLineOk {
   981  			t.Errorf("%s\ngot  Line=%d\nwant %d", testName, got.Body.StackFrames[0].Line, wantStartLine)
   982  		}
   983  		if wantFrames > 0 && wantStartName != "" && got.Body.StackFrames[0].Name != wantStartName {
   984  			t.Errorf("%s\ngot  Name=%s\nwant %s", testName, got.Body.StackFrames[0].Name, wantStartName)
   985  		}
   986  	}
   987  }
   988  
   989  // checkScope is a helper for verifying the values within a ScopesResponse.
   990  //
   991  //	i - index of the scope within ScopesResponse.Body.Scopes array
   992  //	name - name of the scope
   993  //	varRef - reference to retrieve variables of this scope. If varRef is negative, the reference is not checked.
   994  func checkScope(t *testing.T, got *dap.ScopesResponse, i int, name string, varRef int) {
   995  	t.Helper()
   996  	if len(got.Body.Scopes) <= i {
   997  		t.Errorf("\ngot  %d\nwant len(Scopes)>%d", len(got.Body.Scopes), i)
   998  	}
   999  	goti := got.Body.Scopes[i]
  1000  	if goti.Name != name || (varRef >= 0 && goti.VariablesReference != varRef) || goti.Expensive {
  1001  		t.Errorf("\ngot  %#v\nwant Name=%q VariablesReference=%d Expensive=false", goti, name, varRef)
  1002  	}
  1003  }
  1004  
  1005  // checkChildren is a helper for verifying the number of variables within a VariablesResponse.
  1006  //
  1007  //	parentName - pseudoname of the enclosing variable or scope (used for error message only)
  1008  //	numChildren - number of variables/fields/elements of this variable
  1009  func checkChildren(t *testing.T, got *dap.VariablesResponse, parentName string, numChildren int) {
  1010  	t.Helper()
  1011  	if got.Body.Variables == nil {
  1012  		t.Errorf("\ngot  %s children=%#v want []", parentName, got.Body.Variables)
  1013  	}
  1014  	if len(got.Body.Variables) != numChildren {
  1015  		t.Errorf("\ngot  len(%s)=%d (children=%#v)\nwant len=%d", parentName, len(got.Body.Variables), got.Body.Variables, numChildren)
  1016  	}
  1017  }
  1018  
  1019  // checkVar is a helper for verifying the values within a VariablesResponse.
  1020  //
  1021  //	i - index of the variable within VariablesResponse.Body.Variables array (-1 will search all vars for a match)
  1022  //	name - name of the variable
  1023  //	evalName - fully qualified variable name or alternative expression to load this variable
  1024  //	value - the value of the variable
  1025  //	useExactMatch - true if name, evalName and value are to be compared to exactly, false if to be used as regex
  1026  //	hasRef - true if the variable should have children and therefore a non-0 variable reference
  1027  //	ref - reference to retrieve children of this variable (0 if none)
  1028  func checkVar(t *testing.T, got *dap.VariablesResponse, i int, name, evalName, value, typ string, useExactMatch, hasRef bool, indexed, named int) (ref int) {
  1029  	t.Helper()
  1030  	if len(got.Body.Variables) <= i {
  1031  		t.Errorf("\ngot  len=%d (children=%#v)\nwant len>%d", len(got.Body.Variables), got.Body.Variables, i)
  1032  		return
  1033  	}
  1034  	if i < 0 {
  1035  		for vi, v := range got.Body.Variables {
  1036  			if v.Name == name {
  1037  				i = vi
  1038  				break
  1039  			}
  1040  		}
  1041  	}
  1042  	if i < 0 {
  1043  		t.Errorf("\ngot  %#v\nwant Variables[i].Name=%q (not found)", got, name)
  1044  		return 0
  1045  	}
  1046  
  1047  	goti := got.Body.Variables[i]
  1048  	matchedName := false
  1049  	if useExactMatch {
  1050  		if strings.HasPrefix(name, "~r") {
  1051  			matchedName = strings.HasPrefix(goti.Name, "~r")
  1052  		} else {
  1053  			matchedName = (goti.Name == name)
  1054  		}
  1055  	} else {
  1056  		matchedName, _ = regexp.MatchString(name, goti.Name)
  1057  	}
  1058  	if !matchedName || (goti.VariablesReference > 0) != hasRef {
  1059  		t.Errorf("\ngot  %#v\nwant Name=%q hasRef=%t", goti, name, hasRef)
  1060  	}
  1061  	matchedEvalName := false
  1062  	if useExactMatch {
  1063  		matchedEvalName = (goti.EvaluateName == evalName)
  1064  	} else {
  1065  		matchedEvalName, _ = regexp.MatchString(evalName, goti.EvaluateName)
  1066  	}
  1067  	if !matchedEvalName {
  1068  		t.Errorf("\ngot  %q\nwant EvaluateName=%q", goti.EvaluateName, evalName)
  1069  	}
  1070  	matchedValue := false
  1071  	if useExactMatch {
  1072  		matchedValue = (goti.Value == value)
  1073  	} else {
  1074  		matchedValue, _ = regexp.MatchString(value, goti.Value)
  1075  	}
  1076  	if !matchedValue {
  1077  		t.Errorf("\ngot  %s=%q\nwant %q", name, goti.Value, value)
  1078  	}
  1079  	matchedType := false
  1080  	if useExactMatch {
  1081  		matchedType = (goti.Type == typ)
  1082  	} else {
  1083  		matchedType, _ = regexp.MatchString(typ, goti.Type)
  1084  	}
  1085  	if !matchedType {
  1086  		t.Errorf("\ngot  %s=%q\nwant %q", name, goti.Type, typ)
  1087  	}
  1088  	if indexed >= 0 && goti.IndexedVariables != indexed {
  1089  		t.Errorf("\ngot  %s=%d indexed\nwant %d indexed", name, goti.IndexedVariables, indexed)
  1090  	}
  1091  	if named >= 0 && goti.NamedVariables != named {
  1092  		t.Errorf("\ngot  %s=%d named\nwant %d named", name, goti.NamedVariables, named)
  1093  	}
  1094  	return goti.VariablesReference
  1095  }
  1096  
  1097  // checkVarExact is a helper like checkVar that matches value exactly.
  1098  func checkVarExact(t *testing.T, got *dap.VariablesResponse, i int, name, evalName, value, typ string, hasRef bool) (ref int) {
  1099  	t.Helper()
  1100  	return checkVarExactIndexed(t, got, i, name, evalName, value, typ, hasRef, -1, -1)
  1101  }
  1102  
  1103  // checkVarExact is a helper like checkVar that matches value exactly.
  1104  func checkVarExactIndexed(t *testing.T, got *dap.VariablesResponse, i int, name, evalName, value, typ string, hasRef bool, indexed, named int) (ref int) {
  1105  	t.Helper()
  1106  	return checkVar(t, got, i, name, evalName, value, typ, true, hasRef, indexed, named)
  1107  }
  1108  
  1109  // checkVarRegex is a helper like checkVar that treats value, evalName or name as a regex.
  1110  func checkVarRegex(t *testing.T, got *dap.VariablesResponse, i int, name, evalName, value, typ string, hasRef bool) (ref int) {
  1111  	t.Helper()
  1112  	return checkVarRegexIndexed(t, got, i, name, evalName, value, typ, hasRef, -1, -1)
  1113  }
  1114  
  1115  // checkVarRegex is a helper like checkVar that treats value, evalName or name as a regex.
  1116  func checkVarRegexIndexed(t *testing.T, got *dap.VariablesResponse, i int, name, evalName, value, typ string, hasRef bool, indexed, named int) (ref int) {
  1117  	t.Helper()
  1118  	return checkVar(t, got, i, name, evalName, value, typ, false, hasRef, indexed, named)
  1119  }
  1120  
  1121  func expectMessageFilterStopped(t *testing.T, client *daptest.Client) dap.Message {
  1122  	msg := client.ExpectMessage(t)
  1123  	if _, isStopped := msg.(*dap.StoppedEvent); isStopped {
  1124  		msg = client.ExpectMessage(t)
  1125  	}
  1126  	return msg
  1127  }
  1128  
  1129  // validateEvaluateName issues an evaluate request with evaluateName of a variable and
  1130  // confirms that it succeeds and returns the same variable record as the original.
  1131  func validateEvaluateName(t *testing.T, client *daptest.Client, got *dap.VariablesResponse, i int) {
  1132  	t.Helper()
  1133  	original := got.Body.Variables[i]
  1134  	client.EvaluateRequest(original.EvaluateName, 1000, "this context will be ignored")
  1135  	validated := client.ExpectEvaluateResponse(t)
  1136  	if original.VariablesReference == 0 && validated.Body.VariablesReference != 0 ||
  1137  		original.VariablesReference != 0 && validated.Body.VariablesReference == 0 {
  1138  		t.Errorf("\ngot  varref=%d\nwant %d", validated.Body.VariablesReference, original.VariablesReference)
  1139  	}
  1140  	// The variable might not be fully loaded, and when we reload it with an expression
  1141  	// more of the subvalues might be revealed, so we must match the loaded prefix only.
  1142  	if strings.Contains(original.Value, "...") {
  1143  		origLoaded := strings.Split(original.Value, "...")[0]
  1144  		if !strings.HasPrefix(validated.Body.Result, origLoaded) {
  1145  			t.Errorf("\ngot  value=%q\nwant %q", validated.Body.Result, original.Value)
  1146  		}
  1147  	} else if original.Value != validated.Body.Result {
  1148  		t.Errorf("\ngot  value=%q\nwant %q", validated.Body.Result, original.Value)
  1149  	}
  1150  }
  1151  
  1152  // TestStackTraceRequest executes to a breakpoint and tests different
  1153  // good and bad configurations of 'stackTrace' requests.
  1154  func TestStackTraceRequest(t *testing.T) {
  1155  	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
  1156  		var stResp *dap.StackTraceResponse
  1157  		runDebugSessionWithBPs(t, client, "launch",
  1158  			// Launch
  1159  			func() {
  1160  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  1161  			},
  1162  			// Set breakpoints
  1163  			fixture.Source, []int{8, 18},
  1164  			[]onBreakpoint{{
  1165  				// Stop at line 8
  1166  				execute: func() {
  1167  					// Even though the stack frames do not change,
  1168  					// repeated requests at the same breakpoint
  1169  					// would assign next block of unique ids to them each time.
  1170  					const NumFrames = 6
  1171  					reqIndex := 0
  1172  					frameID := func() int {
  1173  						return startHandle + reqIndex
  1174  					}
  1175  
  1176  					tests := map[string]struct {
  1177  						startFrame          int
  1178  						levels              int
  1179  						wantStartName       string
  1180  						wantStartLine       int
  1181  						wantStartFrame      int
  1182  						wantFramesReturned  int
  1183  						wantFramesAvailable int
  1184  						exact               bool
  1185  					}{
  1186  						"all frame levels from 0 to NumFrames":    {0, NumFrames, "main.Increment", 8, 0, NumFrames, NumFrames, true},
  1187  						"subset of frames from 1 to -1":           {1, NumFrames - 1, "main.Increment", 11, 1, NumFrames - 1, NumFrames, true},
  1188  						"load stack in pages: first half":         {0, NumFrames / 2, "main.Increment", 8, 0, NumFrames / 2, NumFrames, false},
  1189  						"load stack in pages: second half":        {NumFrames / 2, NumFrames, "main.main", 17, NumFrames / 2, NumFrames / 2, NumFrames, true},
  1190  						"zero levels means all levels":            {0, 0, "main.Increment", 8, 0, NumFrames, NumFrames, true},
  1191  						"zero levels means all remaining levels":  {NumFrames / 2, 0, "main.main", 17, NumFrames / 2, NumFrames / 2, NumFrames, true},
  1192  						"negative levels treated as 0 (all)":      {0, -10, "main.Increment", 8, 0, NumFrames, NumFrames, true},
  1193  						"OOB levels is capped at available len":   {0, NumFrames + 1, "main.Increment", 8, 0, NumFrames, NumFrames, true},
  1194  						"OOB levels is capped at available len 1": {1, NumFrames + 1, "main.Increment", 11, 1, NumFrames - 1, NumFrames, true},
  1195  						"negative startFrame treated as 0":        {-10, 0, "main.Increment", 8, 0, NumFrames, NumFrames, true},
  1196  						"OOB startFrame returns empty trace":      {NumFrames, 0, "main.Increment", -1, -1, 0, NumFrames, true},
  1197  					}
  1198  					for name, tc := range tests {
  1199  						client.StackTraceRequest(1, tc.startFrame, tc.levels)
  1200  						stResp = client.ExpectStackTraceResponse(t)
  1201  						checkStackFramesNamed(name, t, stResp,
  1202  							tc.wantStartName, tc.wantStartLine, frameID(), tc.wantFramesReturned, tc.wantFramesAvailable, tc.exact)
  1203  						reqIndex += len(stResp.Body.StackFrames)
  1204  					}
  1205  				},
  1206  				disconnect: false,
  1207  			}, {
  1208  				// Stop at line 18
  1209  				execute: func() {
  1210  					// Frame ids get reset at each breakpoint.
  1211  					client.StackTraceRequest(1, 0, 0)
  1212  					stResp = client.ExpectStackTraceResponse(t)
  1213  					checkStackFramesExact(t, stResp, "main.main", 18, startHandle, 3, 3)
  1214  				},
  1215  				disconnect: false,
  1216  			}})
  1217  	})
  1218  	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
  1219  		var stResp *dap.StackTraceResponse
  1220  		runDebugSessionWithBPs(t, client, "launch",
  1221  			// Launch
  1222  			func() {
  1223  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  1224  			},
  1225  			// Set breakpoints
  1226  			fixture.Source, []int{8, 18},
  1227  			[]onBreakpoint{{
  1228  				// Stop at line 8
  1229  				execute: func() {
  1230  					// Even though the stack frames do not change,
  1231  					// repeated requests at the same breakpoint
  1232  					// would assign next block of unique ids to them each time.
  1233  					const NumFrames = 6
  1234  
  1235  					var frames []dap.StackFrame
  1236  
  1237  					for start, levels := 0, 1; start < NumFrames; {
  1238  						client.StackTraceRequest(1, start, levels)
  1239  						stResp = client.ExpectStackTraceResponse(t)
  1240  						frames = append(frames, stResp.Body.StackFrames...)
  1241  						if stResp.Body.TotalFrames < NumFrames {
  1242  							t.Errorf("got  %#v\nwant TotalFrames>=%d\n", stResp.Body.TotalFrames, NumFrames)
  1243  						}
  1244  
  1245  						if len(stResp.Body.StackFrames) < levels {
  1246  							t.Errorf("got  len(StackFrames)=%d\nwant >=%d\n", len(stResp.Body.StackFrames), levels)
  1247  						}
  1248  
  1249  						start += len(stResp.Body.StackFrames)
  1250  					}
  1251  
  1252  					// TODO check all the frames.
  1253  					want := []struct {
  1254  						wantName string
  1255  						wantLine int
  1256  					}{
  1257  						{"main.Increment", 8},
  1258  						{"main.Increment", 11},
  1259  						{"main.Increment", 11},
  1260  						{"main.main", 17},
  1261  						{"runtime.main", 0},
  1262  						{"runtime.goexit", 0},
  1263  					}
  1264  					for i, frame := range frames {
  1265  						frameId := startHandle + i
  1266  						if frame.Id != frameId {
  1267  							t.Errorf("got  %#v\nwant Id=%d\n", frame, frameId)
  1268  						}
  1269  
  1270  						// Verify the name and line corresponding to the first returned frame (if any).
  1271  						// This is useful when the first frame is the frame corresponding to the breakpoint at
  1272  						// a predefined line. Line values < 0 are a signal to skip the check (which can be useful
  1273  						// for frames in the third-party code, where we do not control the lines).
  1274  						if want[i].wantLine > 0 && frame.Line != want[i].wantLine {
  1275  							t.Errorf("got  Line=%d\nwant %d\n", frame.Line, want[i].wantLine)
  1276  						}
  1277  						if want[i].wantName != "" && frame.Name != want[i].wantName {
  1278  							t.Errorf("got  Name=%s\nwant %s\n", frame.Name, want[i].wantName)
  1279  						}
  1280  					}
  1281  				},
  1282  				disconnect: false,
  1283  			}, {
  1284  				// Stop at line 18
  1285  				execute: func() {
  1286  					// Frame ids get reset at each breakpoint.
  1287  					client.StackTraceRequest(1, 0, 0)
  1288  					stResp = client.ExpectStackTraceResponse(t)
  1289  					checkStackFramesExact(t, stResp, "main.main", 18, startHandle, 3, 3)
  1290  				},
  1291  				disconnect: false,
  1292  			}})
  1293  	})
  1294  }
  1295  
  1296  func TestFunctionNameFormattingInStackTrace(t *testing.T) {
  1297  	runTest(t, "consts", func(client *daptest.Client, fixture protest.Fixture) {
  1298  		runDebugSessionWithBPs(t, client, "launch",
  1299  			// Launch
  1300  			func() {
  1301  				client.LaunchRequestWithArgs(map[string]interface{}{
  1302  					"mode": "exec", "program": fixture.Path,
  1303  				})
  1304  			},
  1305  			// Breakpoints are set within the program
  1306  			fixture.Source, []int{},
  1307  			[]onBreakpoint{{
  1308  				// Stop at line 36
  1309  				execute: func() {
  1310  					if runtime.GOARCH == "386" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 18) && !goversion.VersionAfterOrEqual(runtime.Version(), 1, 21) {
  1311  						client.StepInRequest(1)
  1312  						client.ExpectStepInResponse(t)
  1313  						client.ExpectStoppedEvent(t)
  1314  					}
  1315  					client.StackTraceRequest(1, 0, 20)
  1316  					stack := client.ExpectStackTraceResponse(t)
  1317  					checkStackFramesExact(t, stack, "main.main", 36, 1000, 3, 3)
  1318  
  1319  					// Step into pkg.AnotherMethod()
  1320  					client.StepInRequest(1)
  1321  					client.ExpectStepInResponse(t)
  1322  					client.ExpectStoppedEvent(t)
  1323  
  1324  					client.StackTraceRequest(1, 0, 20)
  1325  					stack = client.ExpectStackTraceResponse(t)
  1326  					checkStackFramesExact(t, stack, "pkg.(*SomeType).AnotherMethod", 13, 1000, 4, 4)
  1327  				},
  1328  				disconnect: true,
  1329  			}})
  1330  	})
  1331  }
  1332  
  1333  func Test_fnName(t *testing.T) {
  1334  	tests := []struct {
  1335  		symbol string
  1336  		want   string
  1337  	}{
  1338  		{
  1339  			symbol: "pkg.functionName",
  1340  			want:   "pkg.functionName",
  1341  		},
  1342  		{
  1343  			symbol: "github.com/some/long/package/path/pkg.(*SomeType).Method",
  1344  			want:   "pkg.(*SomeType).Method",
  1345  		},
  1346  		{
  1347  			symbol: "github.com/some/path/pkg.typeparametric[go.shape.struct { example.com/blah/otherpkg.x int }]",
  1348  			want:   "pkg.typeparametric[go.shape.struct { example.com/blah/otherpkg.x int }]",
  1349  		},
  1350  	}
  1351  	for _, tt := range tests {
  1352  		t.Run(tt.symbol, func(t *testing.T) {
  1353  			loc := proc.Location{Fn: &proc.Function{Name: tt.symbol}}
  1354  			if got := fnName(&loc); got != tt.want {
  1355  				t.Errorf("fnName() = %v, want %v", got, tt.want)
  1356  			}
  1357  		})
  1358  	}
  1359  }
  1360  
  1361  func TestSelectedThreadsRequest(t *testing.T) {
  1362  	runTest(t, "goroutinestackprog", func(client *daptest.Client, fixture protest.Fixture) {
  1363  		runDebugSessionWithBPs(t, client, "launch",
  1364  			// Launch
  1365  			func() {
  1366  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  1367  			},
  1368  			// Set breakpoints
  1369  			fixture.Source, []int{20},
  1370  			[]onBreakpoint{{
  1371  				execute: func() {
  1372  					checkStop(t, client, 1, "main.main", 20)
  1373  
  1374  					defaultMaxGoroutines := maxGoroutines
  1375  					defer func() { maxGoroutines = defaultMaxGoroutines }()
  1376  
  1377  					maxGoroutines = 1
  1378  					client.SetBreakpointsRequest(fixture.Source, []int{8})
  1379  					client.ExpectSetBreakpointsResponse(t)
  1380  
  1381  					client.ContinueRequest(1)
  1382  					client.ExpectContinueResponse(t)
  1383  
  1384  					se := client.ExpectStoppedEvent(t)
  1385  					if se.Body.Reason != "breakpoint" || se.Body.ThreadId == 1 {
  1386  						t.Errorf("got %#v, want Reason=%q, ThreadId!=1", se, "breakpoint")
  1387  					}
  1388  
  1389  					client.ThreadsRequest()
  1390  					oe := client.ExpectOutputEvent(t)
  1391  					if !strings.HasPrefix(oe.Body.Output, "Too many goroutines") {
  1392  						t.Errorf("got %#v, expected Output=\"Too many goroutines...\"\n", oe)
  1393  					}
  1394  					tr := client.ExpectThreadsResponse(t)
  1395  
  1396  					if len(tr.Body.Threads) != 2 {
  1397  						t.Errorf("got %d threads, expected 2\n", len(tr.Body.Threads))
  1398  					}
  1399  
  1400  					var selectedFound bool
  1401  					for _, thread := range tr.Body.Threads {
  1402  						if thread.Id == se.Body.ThreadId {
  1403  							selectedFound = true
  1404  							break
  1405  						}
  1406  					}
  1407  					if !selectedFound {
  1408  						t.Errorf("got %#v, want ThreadId=%d\n", tr.Body.Threads, se.Body.ThreadId)
  1409  					}
  1410  				},
  1411  				disconnect: true,
  1412  			}})
  1413  	})
  1414  }
  1415  
  1416  func TestGoroutineLabels(t *testing.T) {
  1417  	tests := []struct {
  1418  		showPprofLabelsConfig   []string
  1419  		expectedPrefixWithLabel string
  1420  	}{
  1421  		{[]string{}, "* [Go 1]"},
  1422  		{[]string{"k1"}, "* [Go 1 v1]"},
  1423  		{[]string{"k2"}, "* [Go 1 v2]"},
  1424  		{[]string{"k2", "k1"}, "* [Go 1 k2:v2 k1:v1]"}, // When passing keys explicitly, we show them in the given order
  1425  		{[]string{"unknown"}, "* [Go 1]"},
  1426  		{[]string{"unknown", "k1"}, "* [Go 1 k1:v1]"},
  1427  		{[]string{"*"}, "* [Go 1 k1:v1 k2:v2]"}, // Special case for showing all labels; labels are shown sorted by key
  1428  	}
  1429  	for _, tc := range tests {
  1430  		runTest(t, "goroutineLabels", func(client *daptest.Client, fixture protest.Fixture) {
  1431  			runDebugSessionWithBPs(t, client, "launch",
  1432  				// Launch
  1433  				func() {
  1434  					client.LaunchRequestWithArgs(map[string]interface{}{
  1435  						"mode":                 "exec",
  1436  						"program":              fixture.Path,
  1437  						"hideSystemGoroutines": true,
  1438  						"showPprofLabels":      tc.showPprofLabelsConfig,
  1439  						"stopOnEntry":          !stopOnEntry,
  1440  					})
  1441  				},
  1442  				// Breakpoints are set within the program
  1443  				"", []int{},
  1444  				[]onBreakpoint{{
  1445  					execute: func() {
  1446  						client.ThreadsRequest()
  1447  						tr := client.ExpectThreadsResponse(t)
  1448  						if len(tr.Body.Threads) != 1 {
  1449  							t.Errorf("got %d threads, expected 1\n", len(tr.Body.Threads))
  1450  						}
  1451  						// The first breakpoint is before the call to pprof.Do; no labels yet:
  1452  						expectedPrefix := "* [Go 1]"
  1453  						if !strings.HasPrefix(tr.Body.Threads[0].Name, expectedPrefix) {
  1454  							t.Errorf("got %s, expected %s\n", tr.Body.Threads[0].Name, expectedPrefix)
  1455  						}
  1456  
  1457  						client.ContinueRequest(1)
  1458  						client.ExpectContinueResponse(t)
  1459  						client.ExpectStoppedEvent(t)
  1460  						checkStop(t, client, 1, "main.f", 21)
  1461  						client.ThreadsRequest()
  1462  						tr = client.ExpectThreadsResponse(t)
  1463  						if len(tr.Body.Threads) != 1 {
  1464  							t.Errorf("got %d threads, expected 1\n", len(tr.Body.Threads))
  1465  						}
  1466  						// The second breakpoint is inside pprof.Do, so there are labels:
  1467  						if !strings.HasPrefix(tr.Body.Threads[0].Name, tc.expectedPrefixWithLabel) {
  1468  							t.Errorf("got %s, expected %s\n", tr.Body.Threads[0].Name, tc.expectedPrefixWithLabel)
  1469  						}
  1470  					},
  1471  					disconnect: true,
  1472  				}},
  1473  			)
  1474  		})
  1475  	}
  1476  }
  1477  
  1478  func TestHideSystemGoroutinesRequest(t *testing.T) {
  1479  	tests := []struct{ hideSystemGoroutines bool }{
  1480  		{hideSystemGoroutines: true},
  1481  		{hideSystemGoroutines: false},
  1482  	}
  1483  	for _, tt := range tests {
  1484  		runTest(t, "goroutinestackprog", func(client *daptest.Client, fixture protest.Fixture) {
  1485  			runDebugSessionWithBPs(t, client, "launch",
  1486  				// Launch
  1487  				func() {
  1488  					client.LaunchRequestWithArgs(map[string]interface{}{
  1489  						"mode":                 "exec",
  1490  						"program":              fixture.Path,
  1491  						"hideSystemGoroutines": tt.hideSystemGoroutines,
  1492  						"stopOnEntry":          !stopOnEntry,
  1493  					})
  1494  				},
  1495  				// Set breakpoints
  1496  				fixture.Source, []int{25},
  1497  				[]onBreakpoint{{
  1498  					execute: func() {
  1499  						checkStop(t, client, 1, "main.main", 25)
  1500  
  1501  						client.ThreadsRequest()
  1502  						tr := client.ExpectThreadsResponse(t)
  1503  
  1504  						// The user process creates 10 goroutines in addition to the
  1505  						// main goroutine, for a total of 11 goroutines.
  1506  						userCount := 11
  1507  						if tt.hideSystemGoroutines {
  1508  							if len(tr.Body.Threads) != userCount {
  1509  								t.Errorf("got %d goroutines, expected %d\n", len(tr.Body.Threads), userCount)
  1510  							}
  1511  						} else {
  1512  							if len(tr.Body.Threads) <= userCount {
  1513  								t.Errorf("got %d goroutines, expected >%d\n", len(tr.Body.Threads), userCount)
  1514  							}
  1515  						}
  1516  					},
  1517  					disconnect: true,
  1518  				}})
  1519  		})
  1520  	}
  1521  }
  1522  
  1523  // TestScopesAndVariablesRequests executes to a breakpoint and tests different
  1524  // configurations of 'scopes' and 'variables' requests.
  1525  func TestScopesAndVariablesRequests(t *testing.T) {
  1526  	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {
  1527  		runDebugSessionWithBPs(t, client, "launch",
  1528  			// Launch
  1529  			func() {
  1530  				client.LaunchRequestWithArgs(map[string]interface{}{
  1531  					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true, "backend": "default",
  1532  				})
  1533  			},
  1534  			// Breakpoints are set within the program
  1535  			fixture.Source, []int{},
  1536  			[]onBreakpoint{{
  1537  				// Stop at first breakpoint
  1538  				execute: func() {
  1539  					client.StackTraceRequest(1, 0, 20)
  1540  					stack := client.ExpectStackTraceResponse(t)
  1541  
  1542  					checkStackFramesExact(t, stack, "main.foobar", []int{65, 66}, 1000, 4, 4)
  1543  
  1544  					client.ScopesRequest(1000)
  1545  					scopes := client.ExpectScopesResponse(t)
  1546  					checkScope(t, scopes, 0, "Locals", localsScope)
  1547  					checkScope(t, scopes, 1, "Globals (package main)", globalsScope)
  1548  
  1549  					// Globals
  1550  
  1551  					client.VariablesRequest(globalsScope)
  1552  					globals := client.ExpectVariablesResponse(t)
  1553  					checkVarExact(t, globals, 0, "p1", "main.p1", "10", "int", noChildren)
  1554  
  1555  					// Locals
  1556  
  1557  					client.VariablesRequest(localsScope)
  1558  					locals := client.ExpectVariablesResponse(t)
  1559  					checkChildren(t, locals, "Locals", 33)
  1560  					checkVarExact(t, locals, 0, "baz", "baz", `"bazburzum"`, "string", noChildren)
  1561  					ref := checkVarExact(t, locals, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, "main.FooBar", hasChildren)
  1562  					if ref > 0 {
  1563  						client.VariablesRequest(ref)
  1564  						bar := client.ExpectVariablesResponse(t)
  1565  						checkChildren(t, bar, "bar", 2)
  1566  						checkVarExact(t, bar, 0, "Baz", "bar.Baz", "10", "int", noChildren)
  1567  						checkVarExact(t, bar, 1, "Bur", "bar.Bur", `"lorem"`, "string", noChildren)
  1568  						validateEvaluateName(t, client, bar, 0)
  1569  						validateEvaluateName(t, client, bar, 1)
  1570  					}
  1571  
  1572  					// reflect.Kind == Bool
  1573  					checkVarExact(t, locals, -1, "b1", "b1", "true", "bool", noChildren)
  1574  					checkVarExact(t, locals, -1, "b2", "b2", "false", "bool", noChildren)
  1575  					// reflect.Kind == Int
  1576  					checkVarExact(t, locals, -1, "a2", "a2", "6", "int", noChildren)
  1577  					checkVarExact(t, locals, -1, "neg", "neg", "-1", "int", noChildren)
  1578  					// reflect.Kind == Int8
  1579  					checkVarExact(t, locals, -1, "i8", "i8", "1", "int8", noChildren)
  1580  					// reflect.Kind == Int16 - see testvariables2
  1581  					// reflect.Kind == Int32 - see testvariables2
  1582  					// reflect.Kind == Int64 - see testvariables2
  1583  					// reflect.Kind == Uint
  1584  					// reflect.Kind == Uint8
  1585  					checkVarExact(t, locals, -1, "u8", "u8", "255 = 0xff", "uint8", noChildren)
  1586  					// reflect.Kind == Uint16
  1587  					checkVarExact(t, locals, -1, "u16", "u16", "65535 = 0xffff", "uint16", noChildren)
  1588  					// reflect.Kind == Uint32
  1589  					checkVarExact(t, locals, -1, "u32", "u32", "4294967295 = 0xffffffff", "uint32", noChildren)
  1590  					// reflect.Kind == Uint64
  1591  					checkVarExact(t, locals, -1, "u64", "u64", "18446744073709551615 = 0xffffffffffffffff", "uint64", noChildren)
  1592  					// reflect.Kind == Uintptr
  1593  					checkVarExact(t, locals, -1, "up", "up", "5 = 0x5", "uintptr", noChildren)
  1594  					// reflect.Kind == Float32
  1595  					checkVarExact(t, locals, -1, "f32", "f32", "1.2", "float32", noChildren)
  1596  					// reflect.Kind == Float64
  1597  					checkVarExact(t, locals, -1, "a3", "a3", "7.23", "float64", noChildren)
  1598  					// reflect.Kind == Complex64
  1599  					ref = checkVarExact(t, locals, -1, "c64", "c64", "(1 + 2i)", "complex64", hasChildren)
  1600  					if ref > 0 {
  1601  						client.VariablesRequest(ref)
  1602  						c64 := client.ExpectVariablesResponse(t)
  1603  						checkChildren(t, c64, "c64", 2)
  1604  						checkVarExact(t, c64, 0, "real", "", "1", "float32", noChildren)
  1605  						checkVarExact(t, c64, 1, "imaginary", "", "2", "float32", noChildren)
  1606  					}
  1607  					// reflect.Kind == Complex128
  1608  					ref = checkVarExact(t, locals, -1, "c128", "c128", "(2 + 3i)", "complex128", hasChildren)
  1609  					if ref > 0 {
  1610  						client.VariablesRequest(ref)
  1611  						c128 := client.ExpectVariablesResponse(t)
  1612  						checkChildren(t, c128, "c128", 2)
  1613  						checkVarExact(t, c128, 0, "real", "", "2", "float64", noChildren)
  1614  						checkVarExact(t, c128, 1, "imaginary", "", "3", "float64", noChildren)
  1615  					}
  1616  					// reflect.Kind == Array
  1617  					ref = checkVarExact(t, locals, -1, "a4", "a4", "[2]int [1,2]", "[2]int", hasChildren)
  1618  					if ref > 0 {
  1619  						client.VariablesRequest(ref)
  1620  						a4 := client.ExpectVariablesResponse(t)
  1621  						checkChildren(t, a4, "a4", 2)
  1622  						checkVarExact(t, a4, 0, "[0]", "a4[0]", "1", "int", noChildren)
  1623  						checkVarExact(t, a4, 1, "[1]", "a4[1]", "2", "int", noChildren)
  1624  					}
  1625  					ref = checkVarExact(t, locals, -1, "a11", "a11", `[3]main.FooBar [{Baz: 1, Bur: "a"},{Baz: 2, Bur: "b"},{Baz: 3, Bur: "c"}]`, "[3]main.FooBar", hasChildren)
  1626  					if ref > 0 {
  1627  						client.VariablesRequest(ref)
  1628  						a11 := client.ExpectVariablesResponse(t)
  1629  						checkChildren(t, a11, "a11", 3)
  1630  						checkVarExact(t, a11, 0, "[0]", "a11[0]", `main.FooBar {Baz: 1, Bur: "a"}`, "main.FooBar", hasChildren)
  1631  						ref = checkVarExact(t, a11, 1, "[1]", "a11[1]", `main.FooBar {Baz: 2, Bur: "b"}`, "main.FooBar", hasChildren)
  1632  						if ref > 0 {
  1633  							client.VariablesRequest(ref)
  1634  							a11_1 := client.ExpectVariablesResponse(t)
  1635  							checkChildren(t, a11_1, "a11[1]", 2)
  1636  							checkVarExact(t, a11_1, 0, "Baz", "a11[1].Baz", "2", "int", noChildren)
  1637  							checkVarExact(t, a11_1, 1, "Bur", "a11[1].Bur", `"b"`, "string", noChildren)
  1638  							validateEvaluateName(t, client, a11_1, 0)
  1639  							validateEvaluateName(t, client, a11_1, 1)
  1640  						}
  1641  						checkVarExact(t, a11, 2, "[2]", "a11[2]", `main.FooBar {Baz: 3, Bur: "c"}`, "main.FooBar", hasChildren)
  1642  					}
  1643  
  1644  					// reflect.Kind == Chan - see testvariables2
  1645  					// reflect.Kind == Func - see testvariables2
  1646  					// reflect.Kind == Interface - see testvariables2
  1647  					// reflect.Kind == Map - see testvariables2
  1648  					// reflect.Kind == Ptr
  1649  					ref = checkVarExact(t, locals, -1, "a7", "a7", `*main.FooBar {Baz: 5, Bur: "strum"}`, "*main.FooBar", hasChildren)
  1650  					if ref > 0 {
  1651  						client.VariablesRequest(ref)
  1652  						a7 := client.ExpectVariablesResponse(t)
  1653  						checkChildren(t, a7, "a7", 1)
  1654  						ref = checkVarExact(t, a7, 0, "", "(*a7)", `main.FooBar {Baz: 5, Bur: "strum"}`, "main.FooBar", hasChildren)
  1655  						if ref > 0 {
  1656  							client.VariablesRequest(ref)
  1657  							a7val := client.ExpectVariablesResponse(t)
  1658  							checkChildren(t, a7val, "*a7", 2)
  1659  							checkVarExact(t, a7val, 0, "Baz", "(*a7).Baz", "5", "int", noChildren)
  1660  							checkVarExact(t, a7val, 1, "Bur", "(*a7).Bur", `"strum"`, "string", noChildren)
  1661  							validateEvaluateName(t, client, a7val, 0)
  1662  							validateEvaluateName(t, client, a7val, 1)
  1663  						}
  1664  					}
  1665  					// TODO(polina): how to test for "nil" (without type) and "void"?
  1666  					checkVarExact(t, locals, -1, "a9", "a9", "*main.FooBar nil", "*main.FooBar", noChildren)
  1667  					// reflect.Kind == Slice
  1668  					ref = checkVarExact(t, locals, -1, "a5", "a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "[]int", hasChildren)
  1669  					if ref > 0 {
  1670  						client.VariablesRequest(ref)
  1671  						a5 := client.ExpectVariablesResponse(t)
  1672  						checkChildren(t, a5, "a5", 5)
  1673  						checkVarExact(t, a5, 0, "[0]", "a5[0]", "1", "int", noChildren)
  1674  						checkVarExact(t, a5, 4, "[4]", "a5[4]", "5", "int", noChildren)
  1675  						validateEvaluateName(t, client, a5, 0)
  1676  						validateEvaluateName(t, client, a5, 1)
  1677  					}
  1678  					ref = checkVarExact(t, locals, -1, "a12", "a12", `[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: "d"},{Baz: 5, Bur: "e"}]`, "[]main.FooBar", hasChildren)
  1679  					if ref > 0 {
  1680  						client.VariablesRequest(ref)
  1681  						a12 := client.ExpectVariablesResponse(t)
  1682  						checkChildren(t, a12, "a12", 2)
  1683  						checkVarExact(t, a12, 0, "[0]", "a12[0]", `main.FooBar {Baz: 4, Bur: "d"}`, "main.FooBar", hasChildren)
  1684  						ref = checkVarExact(t, a12, 1, "[1]", "a12[1]", `main.FooBar {Baz: 5, Bur: "e"}`, "main.FooBar", hasChildren)
  1685  						if ref > 0 {
  1686  							client.VariablesRequest(ref)
  1687  							a12_1 := client.ExpectVariablesResponse(t)
  1688  							checkChildren(t, a12_1, "a12[1]", 2)
  1689  							checkVarExact(t, a12_1, 0, "Baz", "a12[1].Baz", "5", "int", noChildren)
  1690  							checkVarExact(t, a12_1, 1, "Bur", "a12[1].Bur", `"e"`, "string", noChildren)
  1691  							validateEvaluateName(t, client, a12_1, 0)
  1692  							validateEvaluateName(t, client, a12_1, 1)
  1693  						}
  1694  					}
  1695  					ref = checkVarExact(t, locals, -1, "a13", "a13", `[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: "f"},*{Baz: 7, Bur: "g"},*{Baz: 8, Bur: "h"}]`, "[]*main.FooBar", hasChildren)
  1696  					if ref > 0 {
  1697  						client.VariablesRequest(ref)
  1698  						a13 := client.ExpectVariablesResponse(t)
  1699  						checkChildren(t, a13, "a13", 3)
  1700  						checkVarExact(t, a13, 0, "[0]", "a13[0]", `*main.FooBar {Baz: 6, Bur: "f"}`, "*main.FooBar", hasChildren)
  1701  						checkVarExact(t, a13, 1, "[1]", "a13[1]", `*main.FooBar {Baz: 7, Bur: "g"}`, "*main.FooBar", hasChildren)
  1702  						ref = checkVarExact(t, a13, 2, "[2]", "a13[2]", `*main.FooBar {Baz: 8, Bur: "h"}`, "*main.FooBar", hasChildren)
  1703  						if ref > 0 {
  1704  							client.VariablesRequest(ref)
  1705  							a13_2 := client.ExpectVariablesResponse(t)
  1706  							checkChildren(t, a13_2, "a13[2]", 1)
  1707  							ref = checkVarExact(t, a13_2, 0, "", "(*a13[2])", `main.FooBar {Baz: 8, Bur: "h"}`, "main.FooBar", hasChildren)
  1708  							validateEvaluateName(t, client, a13_2, 0)
  1709  							if ref > 0 {
  1710  								client.VariablesRequest(ref)
  1711  								val := client.ExpectVariablesResponse(t)
  1712  								checkChildren(t, val, "*a13[2]", 2)
  1713  								checkVarExact(t, val, 0, "Baz", "(*a13[2]).Baz", "8", "int", noChildren)
  1714  								checkVarExact(t, val, 1, "Bur", "(*a13[2]).Bur", `"h"`, "string", noChildren)
  1715  								validateEvaluateName(t, client, val, 0)
  1716  								validateEvaluateName(t, client, val, 1)
  1717  							}
  1718  						}
  1719  					}
  1720  					// reflect.Kind == String
  1721  					checkVarExact(t, locals, -1, "a1", "a1", `"foofoofoofoofoofoo"`, "string", noChildren)
  1722  					checkVarExact(t, locals, -1, "a10", "a10", `"ofo"`, "string", noChildren)
  1723  					// reflect.Kind == Struct
  1724  					ref = checkVarExact(t, locals, -1, "a6", "a6", `main.FooBar {Baz: 8, Bur: "word"}`, "main.FooBar", hasChildren)
  1725  					if ref > 0 {
  1726  						client.VariablesRequest(ref)
  1727  						a6 := client.ExpectVariablesResponse(t)
  1728  						checkChildren(t, a6, "a6", 2)
  1729  						checkVarExact(t, a6, 0, "Baz", "a6.Baz", "8", "int", noChildren)
  1730  						checkVarExact(t, a6, 1, "Bur", "a6.Bur", `"word"`, "string", noChildren)
  1731  					}
  1732  					ref = checkVarExact(t, locals, -1, "a8", "a8", `main.FooBar2 {Bur: 10, Baz: "feh"}`, "main.FooBar2", hasChildren)
  1733  					if ref > 0 {
  1734  						client.VariablesRequest(ref)
  1735  						a8 := client.ExpectVariablesResponse(t)
  1736  						checkChildren(t, a8, "a8", 2)
  1737  						checkVarExact(t, a8, 0, "Bur", "a8.Bur", "10", "int", noChildren)
  1738  						checkVarExact(t, a8, 1, "Baz", "a8.Baz", `"feh"`, "string", noChildren)
  1739  					}
  1740  					// reflect.Kind == UnsafePointer - see testvariables2
  1741  				},
  1742  				disconnect: false,
  1743  			}, {
  1744  				// Stop at second breakpoint
  1745  				execute: func() {
  1746  					// Frame ids get reset at each breakpoint.
  1747  					client.StackTraceRequest(1, 0, 20)
  1748  					stack := client.ExpectStackTraceResponse(t)
  1749  					checkStackFramesExact(t, stack, "main.barfoo", 27, 1000, 5, 5)
  1750  
  1751  					client.ScopesRequest(1000)
  1752  					scopes := client.ExpectScopesResponse(t)
  1753  					checkScope(t, scopes, 0, "Locals", localsScope)
  1754  					checkScope(t, scopes, 1, "Globals (package main)", globalsScope)
  1755  
  1756  					client.ScopesRequest(1111)
  1757  					erres := client.ExpectInvisibleErrorResponse(t)
  1758  					if !checkErrorMessageFormat(erres.Body.Error, "Unable to list locals: unknown frame id 1111") {
  1759  						t.Errorf("\ngot %#v\nwant Format=\"Unable to list locals: unknown frame id 1111\"", erres)
  1760  					}
  1761  
  1762  					client.VariablesRequest(localsScope)
  1763  					locals := client.ExpectVariablesResponse(t)
  1764  					checkChildren(t, locals, "Locals", 1)
  1765  					checkVarExact(t, locals, -1, "a1", "a1", `"bur"`, "string", noChildren)
  1766  
  1767  					client.VariablesRequest(globalsScope)
  1768  					globals := client.ExpectVariablesResponse(t)
  1769  					checkVarExact(t, globals, 0, "p1", "main.p1", "10", "int", noChildren)
  1770  
  1771  					client.VariablesRequest(7777)
  1772  					erres = client.ExpectInvisibleErrorResponse(t)
  1773  					if !checkErrorMessageFormat(erres.Body.Error, "Unable to lookup variable: unknown reference 7777") {
  1774  						t.Errorf("\ngot %#v\nwant Format=\"Unable to lookup variable: unknown reference 7777\"", erres)
  1775  					}
  1776  				},
  1777  				disconnect: false,
  1778  			}})
  1779  	})
  1780  }
  1781  
  1782  // TestScopesAndVariablesRequests2 executes to a breakpoint and tests different
  1783  // configurations of 'scopes' and 'variables' requests.
  1784  func TestScopesAndVariablesRequests2(t *testing.T) {
  1785  	runTest(t, "testvariables2", func(client *daptest.Client, fixture protest.Fixture) {
  1786  		runDebugSessionWithBPs(t, client, "launch",
  1787  			// Launch
  1788  			func() {
  1789  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  1790  			},
  1791  			// Breakpoints are set within the program
  1792  			fixture.Source, []int{},
  1793  			[]onBreakpoint{{
  1794  				execute: func() {
  1795  					client.StackTraceRequest(1, 0, 20)
  1796  					stack := client.ExpectStackTraceResponse(t)
  1797  					checkStackFramesExact(t, stack, "main.main", -1, 1000, 3, 3)
  1798  
  1799  					client.ScopesRequest(1000)
  1800  					scopes := client.ExpectScopesResponse(t)
  1801  					checkScope(t, scopes, 0, "Locals", localsScope)
  1802  				},
  1803  				disconnect: false,
  1804  			}, {
  1805  				execute: func() {
  1806  					client.StackTraceRequest(1, 0, 20)
  1807  					stack := client.ExpectStackTraceResponse(t)
  1808  					checkStackFramesExact(t, stack, "main.main", -1, 1000, 3, 3)
  1809  
  1810  					client.ScopesRequest(1000)
  1811  					scopes := client.ExpectScopesResponse(t)
  1812  					if len(scopes.Body.Scopes) > 1 {
  1813  						t.Errorf("\ngot  %#v\nwant len(scopes)=1 (Arguments & Locals)", scopes)
  1814  					}
  1815  					checkScope(t, scopes, 0, "Locals", localsScope)
  1816  
  1817  					// Locals
  1818  					client.VariablesRequest(localsScope)
  1819  					locals := client.ExpectVariablesResponse(t)
  1820  
  1821  					// reflect.Kind == Bool - see testvariables
  1822  					// reflect.Kind == Int - see testvariables
  1823  					// reflect.Kind == Int8
  1824  					checkVarExact(t, locals, -1, "ni8", "ni8", "-5", "int8", noChildren)
  1825  					// reflect.Kind == Int16
  1826  					checkVarExact(t, locals, -1, "ni16", "ni16", "-5", "int16", noChildren)
  1827  					// reflect.Kind == Int32
  1828  					checkVarExact(t, locals, -1, "ni32", "ni32", "-5", "int32", noChildren)
  1829  					// reflect.Kind == Int64
  1830  					checkVarExact(t, locals, -1, "ni64", "ni64", "-5", "int64", noChildren)
  1831  					// reflect.Kind == Uint
  1832  					// reflect.Kind == Uint8 - see testvariables
  1833  					// reflect.Kind == Uint16 - see testvariables
  1834  					// reflect.Kind == Uint32 - see testvariables
  1835  					// reflect.Kind == Uint64 - see testvariables
  1836  					// reflect.Kind == Uintptr - see testvariables
  1837  					// reflect.Kind == Float32 - see testvariables
  1838  					// reflect.Kind == Float64
  1839  					checkVarExact(t, locals, -1, "pinf", "pinf", "+Inf", "float64", noChildren)
  1840  					checkVarExact(t, locals, -1, "ninf", "ninf", "-Inf", "float64", noChildren)
  1841  					checkVarExact(t, locals, -1, "nan", "nan", "NaN", "float64", noChildren)
  1842  					// reflect.Kind == Complex64 - see testvariables
  1843  					// reflect.Kind == Complex128 - see testvariables
  1844  					// reflect.Kind == Array
  1845  					checkVarExact(t, locals, -1, "a0", "a0", "[0]int []", "[0]int", noChildren)
  1846  					// reflect.Kind == Chan
  1847  					ref := checkVarExact(t, locals, -1, "ch1", "ch1", "chan int 4/11", "chan int", hasChildren)
  1848  					if ref > 0 {
  1849  						client.VariablesRequest(ref)
  1850  						ch1 := client.ExpectVariablesResponse(t)
  1851  						checkVarExact(t, ch1, 0, "qcount", "ch1.qcount", "4 = 0x4", "uint", noChildren)
  1852  						validateEvaluateName(t, client, ch1, 0)
  1853  						validateEvaluateName(t, client, ch1, 10)
  1854  					}
  1855  					checkVarExact(t, locals, -1, "chnil", "chnil", "chan int nil", "chan int", noChildren)
  1856  					// reflect.Kind == Func
  1857  					checkVarExact(t, locals, -1, "fn1", "fn1", "main.afunc", "main.functype", noChildren)
  1858  					checkVarExact(t, locals, -1, "fn2", "fn2", "nil", "main.functype", noChildren)
  1859  					// reflect.Kind == Interface
  1860  					checkVarExact(t, locals, -1, "ifacenil", "ifacenil", "interface {} nil", "interface {}", noChildren)
  1861  					ref = checkVarExact(t, locals, -1, "iface2", "iface2", "interface {}(string) \"test\"", "interface {}(string)", hasChildren)
  1862  					if ref > 0 {
  1863  						client.VariablesRequest(ref)
  1864  						iface2 := client.ExpectVariablesResponse(t)
  1865  						checkChildren(t, iface2, "iface2", 1)
  1866  						checkVarExact(t, iface2, 0, "data", "iface2.(data)", `"test"`, "string", noChildren)
  1867  						validateEvaluateName(t, client, iface2, 0)
  1868  					}
  1869  					ref = checkVarExact(t, locals, -1, "iface4", "iface4", "interface {}([]go/constant.Value) [4]", "interface {}([]go/constant.Value)", hasChildren)
  1870  					if ref > 0 {
  1871  						client.VariablesRequest(ref)
  1872  						iface4 := client.ExpectVariablesResponse(t)
  1873  						checkChildren(t, iface4, "iface4", 1)
  1874  						ref = checkVarExact(t, iface4, 0, "data", "iface4.(data)", "[]go/constant.Value len: 1, cap: 1, [4]", "[]go/constant.Value", hasChildren)
  1875  						if ref > 0 {
  1876  							client.VariablesRequest(ref)
  1877  							iface4data := client.ExpectVariablesResponse(t)
  1878  							checkChildren(t, iface4data, "iface4.data", 1)
  1879  							ref = checkVarExact(t, iface4data, 0, "[0]", "iface4.(data)[0]", "go/constant.Value(go/constant.int64Val) 4", "go/constant.Value(go/constant.int64Val)", hasChildren)
  1880  							if ref > 0 {
  1881  								client.VariablesRequest(ref)
  1882  								iface4data0 := client.ExpectVariablesResponse(t)
  1883  								checkChildren(t, iface4data0, "iface4.data[0]", 1)
  1884  								checkVarExact(t, iface4data0, 0, "data", "iface4.(data)[0].(data)", "4", "go/constant.int64Val", noChildren)
  1885  								validateEvaluateName(t, client, iface4data0, 0)
  1886  							}
  1887  						}
  1888  					}
  1889  					checkVarExact(t, locals, -1, "errnil", "errnil", "error nil", "error", noChildren)
  1890  					ref = checkVarExact(t, locals, -1, "err1", "err1", "error(*main.astruct) *{A: 1, B: 2}", "error(*main.astruct)", hasChildren)
  1891  					if ref > 0 {
  1892  						client.VariablesRequest(ref)
  1893  						err1 := client.ExpectVariablesResponse(t)
  1894  						checkChildren(t, err1, "err1", 1)
  1895  						checkVarExact(t, err1, 0, "data", "err1.(data)", "*main.astruct {A: 1, B: 2}", "*main.astruct", hasChildren)
  1896  						validateEvaluateName(t, client, err1, 0)
  1897  					}
  1898  					ref = checkVarExact(t, locals, -1, "ptrinf", "ptrinf", "*interface {}(**interface {}) **...", "*interface {}", hasChildren)
  1899  					if ref > 0 {
  1900  						client.VariablesRequest(ref)
  1901  						ptrinf_val := client.ExpectVariablesResponse(t)
  1902  						checkChildren(t, ptrinf_val, "*ptrinf", 1)
  1903  						ref = checkVarExact(t, ptrinf_val, 0, "", "(*ptrinf)", "interface {}(**interface {}) **...", "interface {}(**interface {})", hasChildren)
  1904  						if ref > 0 {
  1905  							client.VariablesRequest(ref)
  1906  							ptrinf_val_data := client.ExpectVariablesResponse(t)
  1907  							checkChildren(t, ptrinf_val_data, "(*ptrinf).data", 1)
  1908  							checkVarExact(t, ptrinf_val_data, 0, "data", "(*ptrinf).(data)", "**interface {}(**interface {}) ...", "**interface {}", hasChildren)
  1909  							validateEvaluateName(t, client, ptrinf_val_data, 0)
  1910  						}
  1911  					}
  1912  					// reflect.Kind == Map
  1913  					checkVarExact(t, locals, -1, "mnil", "mnil", "map[string]main.astruct nil", "map[string]main.astruct", noChildren)
  1914  					// key - scalar, value - compound
  1915  					ref = checkVarExact(t, locals, -1, "m2", "m2", "map[int]*main.astruct [1: *{A: 10, B: 11}, ]", "map[int]*main.astruct", hasChildren)
  1916  					if ref > 0 {
  1917  						client.VariablesRequest(ref)
  1918  						m2 := client.ExpectVariablesResponse(t)
  1919  						checkChildren(t, m2, "m2", 2) // each key-value represented by a single child
  1920  						checkVarExact(t, m2, 0, "len()", "len(m2)", "1", "int", noChildren)
  1921  						ref = checkVarExact(t, m2, 1, "1", "m2[1]", "*main.astruct {A: 10, B: 11}", "int: *main.astruct", hasChildren)
  1922  						if ref > 0 {
  1923  							client.VariablesRequest(ref)
  1924  							m2kv1 := client.ExpectVariablesResponse(t)
  1925  							checkChildren(t, m2kv1, "m2[1]", 1)
  1926  							ref = checkVarExact(t, m2kv1, 0, "", "(*m2[1])", "main.astruct {A: 10, B: 11}", "main.astruct", hasChildren)
  1927  							if ref > 0 {
  1928  								client.VariablesRequest(ref)
  1929  								m2kv1deref := client.ExpectVariablesResponse(t)
  1930  								checkChildren(t, m2kv1deref, "*m2[1]", 2)
  1931  								checkVarExact(t, m2kv1deref, 0, "A", "(*m2[1]).A", "10", "int", noChildren)
  1932  								checkVarExact(t, m2kv1deref, 1, "B", "(*m2[1]).B", "11", "int", noChildren)
  1933  								validateEvaluateName(t, client, m2kv1deref, 0)
  1934  								validateEvaluateName(t, client, m2kv1deref, 1)
  1935  							}
  1936  						}
  1937  					}
  1938  					// key - compound, value - scalar
  1939  					ref = checkVarExact(t, locals, -1, "m3", "m3", "map[main.astruct]int [{A: 1, B: 1}: 42, {A: 2, B: 2}: 43, ]", "map[main.astruct]int", hasChildren)
  1940  					if ref > 0 {
  1941  						client.VariablesRequest(ref)
  1942  						m3 := client.ExpectVariablesResponse(t)
  1943  						checkChildren(t, m3, "m3", 3) // each key-value represented by a single child
  1944  						checkVarExact(t, m3, 0, "len()", "len(m3)", "2", "int", noChildren)
  1945  						ref = checkVarRegex(t, m3, 1, `main\.astruct {A: 1, B: 1}`, `m3\[\(\*\(\*"main.astruct"\)\(0x[0-9a-f]+\)\)\]`, "42", "int", hasChildren)
  1946  						if ref > 0 {
  1947  							client.VariablesRequest(ref)
  1948  							m3kv0 := client.ExpectVariablesResponse(t)
  1949  							checkChildren(t, m3kv0, "m3[0]", 2)
  1950  							checkVarRegex(t, m3kv0, 0, "A", `\(*\(*"main\.astruct"\)\(0x[0-9a-f]+\)\)\.A`, "1", "int", noChildren)
  1951  							validateEvaluateName(t, client, m3kv0, 0)
  1952  						}
  1953  						ref = checkVarRegex(t, m3, 2, `main\.astruct {A: 2, B: 2}`, `m3\[\(\*\(\*"main.astruct"\)\(0x[0-9a-f]+\)\)\]`, "43", "", hasChildren)
  1954  						if ref > 0 { // inspect another key from another key-value child
  1955  							client.VariablesRequest(ref)
  1956  							m3kv1 := client.ExpectVariablesResponse(t)
  1957  							checkChildren(t, m3kv1, "m3[1]", 2)
  1958  							checkVarRegex(t, m3kv1, 1, "B", `\(*\(*"main\.astruct"\)\(0x[0-9a-f]+\)\)\.B`, "2", "int", noChildren)
  1959  							validateEvaluateName(t, client, m3kv1, 1)
  1960  						}
  1961  					}
  1962  					// key - compound, value - compound
  1963  					ref = checkVarExact(t, locals, -1, "m4", "m4", "map[main.astruct]main.astruct [{A: 1, B: 1}: {A: 11, B: 11}, {A: 2, B: 2}: {A: 22, B: 22}, ]", "map[main.astruct]main.astruct", hasChildren)
  1964  					if ref > 0 {
  1965  						client.VariablesRequest(ref)
  1966  						m4 := client.ExpectVariablesResponse(t)
  1967  						checkChildren(t, m4, "m4", 5) // each key and value represented by a child, so double the key-value count
  1968  						checkVarExact(t, m4, 0, "len()", "len(m4)", "2", "int", noChildren)
  1969  						checkVarRegex(t, m4, 1, `\[key 0\]`, `\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)`, `main\.astruct {A: 1, B: 1}`, `main\.astruct`, hasChildren)
  1970  						checkVarRegex(t, m4, 2, `\[val 0\]`, `m4\[\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\]`, `main\.astruct {A: 11, B: 11}`, `main\.astruct`, hasChildren)
  1971  						ref = checkVarRegex(t, m4, 3, `\[key 1\]`, `\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)`, `main\.astruct {A: 2, B: 2}`, `main\.astruct`, hasChildren)
  1972  						if ref > 0 {
  1973  							client.VariablesRequest(ref)
  1974  							m4Key1 := client.ExpectVariablesResponse(t)
  1975  							checkChildren(t, m4Key1, "m4Key1", 2)
  1976  							checkVarRegex(t, m4Key1, 0, "A", `\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\.A`, "2", "int", noChildren)
  1977  							checkVarRegex(t, m4Key1, 1, "B", `\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\.B`, "2", "int", noChildren)
  1978  							validateEvaluateName(t, client, m4Key1, 0)
  1979  							validateEvaluateName(t, client, m4Key1, 1)
  1980  						}
  1981  						ref = checkVarRegex(t, m4, 4, `\[val 1\]`, `m4\[\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\]`, `main\.astruct {A: 22, B: 22}`, "main.astruct", hasChildren)
  1982  						if ref > 0 {
  1983  							client.VariablesRequest(ref)
  1984  							m4Val1 := client.ExpectVariablesResponse(t)
  1985  							checkChildren(t, m4Val1, "m4Val1", 2)
  1986  							checkVarRegex(t, m4Val1, 0, "A", `m4\[\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\]\.A`, "22", "int", noChildren)
  1987  							checkVarRegex(t, m4Val1, 1, "B", `m4\[\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\]\.B`, "22", "int", noChildren)
  1988  							validateEvaluateName(t, client, m4Val1, 0)
  1989  							validateEvaluateName(t, client, m4Val1, 1)
  1990  						}
  1991  					}
  1992  					checkVarExact(t, locals, -1, "emptymap", "emptymap", "map[string]string []", "map[string]string", noChildren)
  1993  					// reflect.Kind == Ptr
  1994  					ref = checkVarExact(t, locals, -1, "pp1", "pp1", "**1", "**int", hasChildren)
  1995  					if ref > 0 {
  1996  						client.VariablesRequest(ref)
  1997  						pp1val := client.ExpectVariablesResponse(t)
  1998  						checkChildren(t, pp1val, "*pp1", 1)
  1999  						ref = checkVarExact(t, pp1val, 0, "", "(*pp1)", "*1", "*int", hasChildren)
  2000  						if ref > 0 {
  2001  							client.VariablesRequest(ref)
  2002  							pp1valval := client.ExpectVariablesResponse(t)
  2003  							checkChildren(t, pp1valval, "*(*pp1)", 1)
  2004  							checkVarExact(t, pp1valval, 0, "", "(*(*pp1))", "1", "int", noChildren)
  2005  							validateEvaluateName(t, client, pp1valval, 0)
  2006  						}
  2007  					}
  2008  					// reflect.Kind == Slice
  2009  					ref = checkVarExact(t, locals, -1, "zsslice", "zsslice", "[]struct {} len: 3, cap: 3, [{},{},{}]", "[]struct {}", hasChildren)
  2010  					if ref > 0 {
  2011  						client.VariablesRequest(ref)
  2012  						zsslice := client.ExpectVariablesResponse(t)
  2013  						checkChildren(t, zsslice, "zsslice", 3)
  2014  						checkVarExact(t, zsslice, 2, "[2]", "zsslice[2]", "struct {} {}", "struct {}", noChildren)
  2015  						validateEvaluateName(t, client, zsslice, 2)
  2016  					}
  2017  					checkVarExact(t, locals, -1, "emptyslice", "emptyslice", "[]string len: 0, cap: 0, []", "[]string", noChildren)
  2018  					checkVarExact(t, locals, -1, "nilslice", "nilslice", "[]int len: 0, cap: 0, nil", "[]int", noChildren)
  2019  					// reflect.Kind == String
  2020  					checkVarExact(t, locals, -1, "longstr", "longstr", longstr, "string", noChildren)
  2021  					// reflect.Kind == Struct
  2022  					checkVarExact(t, locals, -1, "zsvar", "zsvar", "struct {} {}", "struct {}", noChildren)
  2023  					// reflect.Kind == UnsafePointer
  2024  					// TODO(polina): how do I test for unsafe.Pointer(nil)?
  2025  					checkVarRegex(t, locals, -1, "upnil", "upnil", `unsafe\.Pointer\(0x0\)`, "int", noChildren)
  2026  					checkVarRegex(t, locals, -1, "up1", "up1", `unsafe\.Pointer\(0x[0-9a-f]+\)`, "int", noChildren)
  2027  
  2028  					// Test unreadable variable
  2029  					ref = checkVarRegex(t, locals, -1, "unread", "unread", `\*\(unreadable .+\)`, "int", hasChildren)
  2030  					if ref > 0 {
  2031  						client.VariablesRequest(ref)
  2032  						val := client.ExpectVariablesResponse(t)
  2033  						checkChildren(t, val, "*unread", 1)
  2034  						checkVarRegex(t, val, 0, "^$", `\(\*unread\)`, `\(unreadable .+\)`, "int", noChildren)
  2035  						validateEvaluateName(t, client, val, 0)
  2036  					}
  2037  				},
  2038  				disconnect: true,
  2039  			}})
  2040  	})
  2041  }
  2042  
  2043  // TestScopesRequestsOptimized executes to a breakpoint and tests different
  2044  // that the name of the "Locals" scope is correctly annotated with
  2045  // a warning about debugging an optimized function.
  2046  func TestScopesRequestsOptimized(t *testing.T) {
  2047  	runTestBuildFlags(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {
  2048  		runDebugSessionWithBPs(t, client, "launch",
  2049  			// Launch
  2050  			func() {
  2051  				client.LaunchRequestWithArgs(map[string]interface{}{
  2052  					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,
  2053  				})
  2054  			},
  2055  			// Breakpoints are set within the program
  2056  			fixture.Source, []int{},
  2057  			[]onBreakpoint{{
  2058  				// Stop at first breakpoint
  2059  				execute: func() {
  2060  					client.StackTraceRequest(1, 0, 20)
  2061  					stack := client.ExpectStackTraceResponse(t)
  2062  
  2063  					checkStackFramesExact(t, stack, "main.foobar", -1, 1000, 4, 4)
  2064  
  2065  					client.ScopesRequest(1000)
  2066  					scopes := client.ExpectScopesResponse(t)
  2067  					checkScope(t, scopes, 0, "Locals (warning: optimized function)", localsScope)
  2068  					checkScope(t, scopes, 1, "Globals (package main)", globalsScope)
  2069  				},
  2070  				disconnect: false,
  2071  			}, {
  2072  				// Stop at second breakpoint
  2073  				execute: func() {
  2074  					// Frame ids get reset at each breakpoint.
  2075  					client.StackTraceRequest(1, 0, 20)
  2076  					stack := client.ExpectStackTraceResponse(t)
  2077  					checkStackFramesExact(t, stack, "main.barfoo", 27, 1000, 5, 5)
  2078  
  2079  					client.ScopesRequest(1000)
  2080  					scopes := client.ExpectScopesResponse(t)
  2081  					checkScope(t, scopes, 0, "Locals (warning: optimized function)", localsScope)
  2082  					checkScope(t, scopes, 1, "Globals (package main)", globalsScope)
  2083  				},
  2084  				disconnect: false,
  2085  			}})
  2086  	},
  2087  		protest.EnableOptimization, false)
  2088  }
  2089  
  2090  // TestVariablesLoading exposes test cases where variables might be partially or
  2091  // fully unloaded.
  2092  func TestVariablesLoading(t *testing.T) {
  2093  	runTest(t, "testvariables2", func(client *daptest.Client, fixture protest.Fixture) {
  2094  		runDebugSessionWithBPs(t, client, "launch",
  2095  			// Launch
  2096  			func() {
  2097  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  2098  			},
  2099  			// Breakpoints are set within the program
  2100  			fixture.Source, []int{},
  2101  			[]onBreakpoint{{
  2102  				execute:    func() {},
  2103  				disconnect: false,
  2104  			}, {
  2105  				execute: func() {
  2106  					// Change default config values to trigger certain unloaded corner cases
  2107  					saveDefaultConfig := DefaultLoadConfig
  2108  					DefaultLoadConfig.MaxStructFields = 5
  2109  					DefaultLoadConfig.MaxStringLen = 64
  2110  					// Set the MaxArrayValues = 33 to execute a bug for map handling where
  2111  					// a request for  2*MaxArrayValues indexed map children would not correctly
  2112  					// reslice the map. See https://github.com/golang/vscode-go/issues/2351.
  2113  					DefaultLoadConfig.MaxArrayValues = 33
  2114  					defer func() {
  2115  						DefaultLoadConfig = saveDefaultConfig
  2116  					}()
  2117  
  2118  					client.StackTraceRequest(1, 0, 0)
  2119  					client.ExpectStackTraceResponse(t)
  2120  
  2121  					client.ScopesRequest(1000)
  2122  					client.ExpectScopesResponse(t)
  2123  
  2124  					client.VariablesRequest(localsScope)
  2125  					locals := client.ExpectVariablesResponse(t)
  2126  
  2127  					// String partially missing based on LoadConfig.MaxStringLen
  2128  					// See also TestVariableLoadingOfLongStrings
  2129  					checkVarExact(t, locals, -1, "longstr", "longstr", longstrLoaded64, "string", noChildren)
  2130  
  2131  					checkArrayChildren := func(t *testing.T, longarr *dap.VariablesResponse, parentName string, start int) {
  2132  						t.Helper()
  2133  						for i, child := range longarr.Body.Variables {
  2134  							idx := start + i
  2135  							if child.Name != fmt.Sprintf("[%d]", idx) || child.EvaluateName != fmt.Sprintf("%s[%d]", parentName, idx) {
  2136  								t.Errorf("Expected %s[%d] to have Name=\"[%d]\" EvaluateName=\"%s[%d]\", got %#v", parentName, idx, idx, parentName, idx, child)
  2137  							}
  2138  						}
  2139  					}
  2140  
  2141  					// Array not fully loaded based on LoadConfig.MaxArrayValues.
  2142  					// Expect to be able to load array by paging.
  2143  					ref := checkVarExactIndexed(t, locals, -1, "longarr", "longarr", "[100]int [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+67 more]", "[100]int", hasChildren, 100, 0)
  2144  					if ref > 0 {
  2145  						client.VariablesRequest(ref)
  2146  						longarr := client.ExpectVariablesResponse(t)
  2147  						checkChildren(t, longarr, "longarr", 33)
  2148  						checkArrayChildren(t, longarr, "longarr", 0)
  2149  
  2150  						client.IndexedVariablesRequest(ref, 0, 100)
  2151  						longarr = client.ExpectVariablesResponse(t)
  2152  						checkChildren(t, longarr, "longarr", 100)
  2153  						checkArrayChildren(t, longarr, "longarr", 0)
  2154  
  2155  						client.IndexedVariablesRequest(ref, 50, 50)
  2156  						longarr = client.ExpectVariablesResponse(t)
  2157  						checkChildren(t, longarr, "longarr", 50)
  2158  						checkArrayChildren(t, longarr, "longarr", 50)
  2159  					}
  2160  
  2161  					// Slice not fully loaded based on LoadConfig.MaxArrayValues.
  2162  					// Expect to be able to load slice by paging.
  2163  					ref = checkVarExactIndexed(t, locals, -1, "longslice", "longslice", "[]int len: 100, cap: 100, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+67 more]", "[]int", hasChildren, 100, 0)
  2164  					if ref > 0 {
  2165  						client.VariablesRequest(ref)
  2166  						longarr := client.ExpectVariablesResponse(t)
  2167  						checkChildren(t, longarr, "longslice", 33)
  2168  						checkArrayChildren(t, longarr, "longslice", 0)
  2169  
  2170  						client.IndexedVariablesRequest(ref, 0, 100)
  2171  						longarr = client.ExpectVariablesResponse(t)
  2172  						checkChildren(t, longarr, "longslice", 100)
  2173  						checkArrayChildren(t, longarr, "longslice", 0)
  2174  
  2175  						client.IndexedVariablesRequest(ref, 50, 50)
  2176  						longarr = client.ExpectVariablesResponse(t)
  2177  						checkChildren(t, longarr, "longslice", 50)
  2178  						checkArrayChildren(t, longarr, "longslice", 50)
  2179  					}
  2180  
  2181  					// Map not fully loaded based on LoadConfig.MaxArrayValues
  2182  					// Expect to be able to load map by paging.
  2183  					ref = checkVarRegexIndexed(t, locals, -1, "m1", "m1", `map\[string\]main\.astruct \[.+\.\.\.`, `map\[string\]main\.astruct`, hasChildren, 66, 1)
  2184  					if ref > 0 {
  2185  						client.VariablesRequest(ref)
  2186  						m1 := client.ExpectVariablesResponse(t)
  2187  						checkChildren(t, m1, "m1", 34)
  2188  
  2189  						client.IndexedVariablesRequest(ref, 0, 66)
  2190  						m1 = client.ExpectVariablesResponse(t)
  2191  						checkChildren(t, m1, "m1", 66)
  2192  
  2193  						client.IndexedVariablesRequest(ref, 0, 33)
  2194  						m1part1 := client.ExpectVariablesResponse(t)
  2195  						checkChildren(t, m1part1, "m1", 33)
  2196  
  2197  						client.IndexedVariablesRequest(ref, 33, 33)
  2198  						m1part2 := client.ExpectVariablesResponse(t)
  2199  						checkChildren(t, m1part2, "m1", 33)
  2200  
  2201  						if len(m1part1.Body.Variables)+len(m1part2.Body.Variables) == len(m1.Body.Variables) {
  2202  							for i, got := range m1part1.Body.Variables {
  2203  								want := m1.Body.Variables[i]
  2204  								if got.Name != want.Name || got.Value != want.Value {
  2205  									t.Errorf("got %#v, want Name=%q Value=%q", got, want.Name, want.Value)
  2206  								}
  2207  							}
  2208  							for i, got := range m1part2.Body.Variables {
  2209  								want := m1.Body.Variables[i+len(m1part1.Body.Variables)]
  2210  								if got.Name != want.Name || got.Value != want.Value {
  2211  									t.Errorf("got %#v, want Name=%q Value=%q", got, want.Name, want.Value)
  2212  								}
  2213  							}
  2214  						}
  2215  						client.NamedVariablesRequest(ref)
  2216  						named := client.ExpectVariablesResponse(t)
  2217  						checkChildren(t, named, "m1", 1)
  2218  						checkVarExact(t, named, 0, "len()", "len(m1)", "66", "int", noChildren)
  2219  					}
  2220  
  2221  					// Struct partially missing based on LoadConfig.MaxStructFields
  2222  					ref = checkVarExact(t, locals, -1, "sd", "sd", "(loaded 5/6) main.D {u1: 0, u2: 0, u3: 0, u4: 0, u5: 0,...+1 more}", "main.D", hasChildren)
  2223  					if ref > 0 {
  2224  						client.VariablesRequest(ref)
  2225  						sd := client.ExpectVariablesResponse(t)
  2226  						checkChildren(t, sd, "sd", 5)
  2227  					}
  2228  
  2229  					// Fully missing struct auto-loaded when reaching LoadConfig.MaxVariableRecurse (also tests evaluateName corner case)
  2230  					ref = checkVarRegex(t, locals, -1, "c1", "c1", `main\.cstruct {pb: \*main\.bstruct {a: \(\*main\.astruct\)\(0x[0-9a-f]+\)}, sa: []\*main\.astruct len: 3, cap: 3, [\*\(\*main\.astruct\)\(0x[0-9a-f]+\),\*\(\*main\.astruct\)\(0x[0-9a-f]+\),\*\(\*main.astruct\)\(0x[0-9a-f]+\)]}`, `main\.cstruct`, hasChildren)
  2231  					if ref > 0 {
  2232  						client.VariablesRequest(ref)
  2233  						c1 := client.ExpectVariablesResponse(t)
  2234  						checkChildren(t, c1, "c1", 2)
  2235  						ref = checkVarRegex(t, c1, 1, "sa", `c1\.sa`, `\[\]\*main\.astruct len: 3, cap: 3, \[\*\(\*main\.astruct\)\(0x[0-9a-f]+\),\*\(\*main\.astruct\)\(0x[0-9a-f]+\),\*\(\*main\.astruct\)\(0x[0-9a-f]+\)\]`, `\[\]\*main\.astruct`, hasChildren)
  2236  						if ref > 0 {
  2237  							client.VariablesRequest(ref)
  2238  							c1sa := client.ExpectVariablesResponse(t)
  2239  							checkChildren(t, c1sa, "c1.sa", 3)
  2240  							ref = checkVarRegex(t, c1sa, 0, `\[0\]`, `c1\.sa\[0\]`, `\*\(\*main\.astruct\)\(0x[0-9a-f]+\)`, `\*main\.astruct`, hasChildren)
  2241  							if ref > 0 {
  2242  								// Auto-loading of fully missing struct children happens here
  2243  								client.VariablesRequest(ref)
  2244  								c1sa0 := client.ExpectVariablesResponse(t)
  2245  								checkChildren(t, c1sa0, "c1.sa[0]", 1)
  2246  								// TODO(polina): there should be children here once we support auto loading
  2247  								checkVarExact(t, c1sa0, 0, "", "(*c1.sa[0])", "main.astruct {A: 1, B: 2}", "main.astruct", hasChildren)
  2248  							}
  2249  						}
  2250  					}
  2251  
  2252  					// Fully missing struct auto-loaded when hitting LoadConfig.MaxVariableRecurse (also tests evaluateName corner case)
  2253  					ref = checkVarRegex(t, locals, -1, "aas", "aas", `\[\]main\.a len: 1, cap: 1, \[{aas: \[\]main\.a len: 1, cap: 1, \[\(\*main\.a\)\(0x[0-9a-f]+\)\]}\]`, `\[\]main\.a`, hasChildren)
  2254  					if ref > 0 {
  2255  						client.VariablesRequest(ref)
  2256  						aas := client.ExpectVariablesResponse(t)
  2257  						checkChildren(t, aas, "aas", 1)
  2258  						ref = checkVarRegex(t, aas, 0, "[0]", `aas\[0\]`, `main\.a {aas: \[\]main.a len: 1, cap: 1, \[\(\*main\.a\)\(0x[0-9a-f]+\)\]}`, `main\.a`, hasChildren)
  2259  						if ref > 0 {
  2260  							client.VariablesRequest(ref)
  2261  							aas0 := client.ExpectVariablesResponse(t)
  2262  							checkChildren(t, aas0, "aas[0]", 1)
  2263  							ref = checkVarRegex(t, aas0, 0, "aas", `aas\[0\]\.aas`, `\[\]main\.a len: 1, cap: 1, \[\(\*main\.a\)\(0x[0-9a-f]+\)\]`, `\[\]main\.a`, hasChildren)
  2264  							if ref > 0 {
  2265  								// Auto-loading of fully missing struct children happens here
  2266  								client.VariablesRequest(ref)
  2267  								aas0aas := client.ExpectVariablesResponse(t)
  2268  								checkChildren(t, aas0aas, "aas[0].aas", 1)
  2269  								// TODO(polina): there should be a child here once we support auto loading - test for "aas[0].aas[0].aas"
  2270  								ref = checkVarRegex(t, aas0aas, 0, "[0]", `aas\[0\]\.aas\[0\]`, `main\.a {aas: \[\]main\.a len: 1, cap: 1, \[\(\*main\.a\)\(0x[0-9a-f]+\)\]}`, "main.a", hasChildren)
  2271  								if ref > 0 {
  2272  									client.VariablesRequest(ref)
  2273  									aas0aas0 := client.ExpectVariablesResponse(t)
  2274  									checkChildren(t, aas0aas, "aas[0].aas[0]", 1)
  2275  									checkVarRegex(t, aas0aas0, 0, "aas", `aas\[0\]\.aas\[0\]\.aas`, `\[\]main\.a len: 1, cap: 1, \[\(\*main\.a\)\(0x[0-9a-f]+\)\]`, `\[\]main\.a`, hasChildren)
  2276  								}
  2277  							}
  2278  						}
  2279  					}
  2280  
  2281  					// Fully missing map auto-loaded when hitting LoadConfig.MaxVariableRecurse (also tests evaluateName corner case)
  2282  					ref = checkVarExact(t, locals, -1, "tm", "tm", "main.truncatedMap {v: []map[string]main.astruct len: 1, cap: 1, [[...]]}", "main.truncatedMap", hasChildren)
  2283  					if ref > 0 {
  2284  						client.VariablesRequest(ref)
  2285  						tm := client.ExpectVariablesResponse(t)
  2286  						checkChildren(t, tm, "tm", 1)
  2287  						ref = checkVarExact(t, tm, 0, "v", "tm.v", "[]map[string]main.astruct len: 1, cap: 1, [[...]]", "[]map[string]main.astruct", hasChildren)
  2288  						if ref > 0 {
  2289  							// Auto-loading of fully missing map chidlren happens here, but they get truncated at MaxArrayValues
  2290  							client.VariablesRequest(ref)
  2291  							tmV := client.ExpectVariablesResponse(t)
  2292  							checkChildren(t, tmV, "tm.v", 1)
  2293  							ref = checkVarRegex(t, tmV, 0, `\[0\]`, `tm\.v\[0\]`, `map\[string\]main\.astruct \[.+\.\.\.`, `map\[string\]main\.astruct`, hasChildren)
  2294  							if ref > 0 {
  2295  								client.VariablesRequest(ref)
  2296  								tmV0 := client.ExpectVariablesResponse(t)
  2297  								checkChildren(t, tmV0, "tm.v[0]", 34)
  2298  							}
  2299  						}
  2300  					}
  2301  
  2302  					// Auto-loading works with call return variables as well
  2303  					protest.MustSupportFunctionCalls(t, testBackend)
  2304  					client.EvaluateRequest("call rettm()", 1000, "repl")
  2305  					got := client.ExpectEvaluateResponse(t)
  2306  					ref = checkEval(t, got, "main.truncatedMap {v: []map[string]main.astruct len: 1, cap: 1, [[...]]}", hasChildren)
  2307  					if ref > 0 {
  2308  						client.VariablesRequest(ref)
  2309  						rv := client.ExpectVariablesResponse(t)
  2310  						checkChildren(t, rv, "rv", 1)
  2311  						ref = checkVarExact(t, rv, 0, "~r0", "", "main.truncatedMap {v: []map[string]main.astruct len: 1, cap: 1, [[...]]}", "main.truncatedMap", hasChildren)
  2312  						if ref > 0 {
  2313  							client.VariablesRequest(ref)
  2314  							tm := client.ExpectVariablesResponse(t)
  2315  							checkChildren(t, tm, "tm", 1)
  2316  							ref = checkVarExact(t, tm, 0, "v", "", "[]map[string]main.astruct len: 1, cap: 1, [[...]]", "[]map[string]main.astruct", hasChildren)
  2317  							if ref > 0 {
  2318  								// Auto-loading of fully missing map chidlren happens here, but they get trancated at MaxArrayValues
  2319  								client.VariablesRequest(ref)
  2320  								tmV := client.ExpectVariablesResponse(t)
  2321  								checkChildren(t, tmV, "tm.v", 1)
  2322  								// TODO(polina): this evaluate name is not usable - it should be empty
  2323  								ref = checkVarRegex(t, tmV, 0, `\[0\]`, `\[0\]`, `map\[string\]main\.astruct \[.+\.\.\.`, `map\[string\]main\.astruct`, hasChildren)
  2324  								if ref > 0 {
  2325  									client.VariablesRequest(ref)
  2326  									tmV0 := client.ExpectVariablesResponse(t)
  2327  									checkChildren(t, tmV0, "tm.v[0]", 34)
  2328  								}
  2329  							}
  2330  						}
  2331  					}
  2332  
  2333  					// TODO(polina): need fully missing array/slice test case
  2334  
  2335  					// Zero slices, structs and maps are not treated as fully missing
  2336  					// See zsvar, zsslice,, emptyslice, emptymap, a0
  2337  				},
  2338  				disconnect: true,
  2339  			}})
  2340  	})
  2341  	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {
  2342  		runDebugSessionWithBPs(t, client, "launch",
  2343  			// Launch
  2344  			func() {
  2345  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  2346  			},
  2347  			// Breakpoints are set within the program
  2348  			fixture.Source, []int{},
  2349  			[]onBreakpoint{{
  2350  				execute: func() {
  2351  					DefaultLoadConfig.FollowPointers = false
  2352  					defer func() { DefaultLoadConfig.FollowPointers = true }()
  2353  
  2354  					client.StackTraceRequest(1, 0, 0)
  2355  					client.ExpectStackTraceResponse(t)
  2356  
  2357  					loadvars := func(frame int) {
  2358  						client.ScopesRequest(frame)
  2359  						scopes := client.ExpectScopesResponse(t)
  2360  						localsRef := 0
  2361  						for _, s := range scopes.Body.Scopes {
  2362  							if s.Name == "Locals" {
  2363  								localsRef = s.VariablesReference
  2364  							}
  2365  						}
  2366  
  2367  						client.VariablesRequest(localsRef)
  2368  						locals := client.ExpectVariablesResponse(t)
  2369  
  2370  						// Interface auto-loaded when hitting LoadConfig.MaxVariableRecurse=1
  2371  
  2372  						ref := checkVarRegex(t, locals, -1, "ni", "ni", `\[\]interface {} len: 1, cap: 1, \[\[\]interface {} len: 1, cap: 1, \[\*\(\*interface {}\)\(0x[0-9a-f]+\)\]\]`, `\[\]interface {}`, hasChildren)
  2373  						if ref > 0 {
  2374  							client.VariablesRequest(ref)
  2375  							ni := client.ExpectVariablesResponse(t)
  2376  							ref = checkVarRegex(t, ni, 0, `\[0\]`, `ni\[0\]`, `interface \{\}\(\[\]interface \{\}\) \[\*\(\*interface \{\}\)\(0x[0-9a-f]+\)\]`, "interface {}", hasChildren)
  2377  							if ref > 0 {
  2378  								client.VariablesRequest(ref)
  2379  								niI1 := client.ExpectVariablesResponse(t)
  2380  								ref = checkVarRegex(t, niI1, 0, "data", `ni\[0\]\.\(data\)`, `\[\]interface {} len: 1, cap: 1, \[\*\(\*interface {}\)\(0x[0-9a-f]+\)`, `\[\]interface {}`, hasChildren)
  2381  								if ref > 0 {
  2382  									// Auto-loading happens here
  2383  									client.VariablesRequest(ref)
  2384  									niI1Data := client.ExpectVariablesResponse(t)
  2385  									ref = checkVarExact(t, niI1Data, 0, "[0]", "ni[0].(data)[0]", "interface {}(int) 123", "interface {}(int)", hasChildren)
  2386  									if ref > 0 {
  2387  										client.VariablesRequest(ref)
  2388  										niI1DataI2 := client.ExpectVariablesResponse(t)
  2389  										checkVarExact(t, niI1DataI2, 0, "data", "ni[0].(data)[0].(data)", "123", "int", noChildren)
  2390  									}
  2391  								}
  2392  							}
  2393  						}
  2394  
  2395  						// Pointer values loaded even with LoadConfig.FollowPointers=false
  2396  						checkVarExact(t, locals, -1, "a7", "a7", "*main.FooBar {Baz: 5, Bur: \"strum\"}", "*main.FooBar", hasChildren)
  2397  
  2398  						// Auto-loading works on results of evaluate expressions as well
  2399  						client.EvaluateRequest("a7", frame, "repl")
  2400  						checkEval(t, client.ExpectEvaluateResponse(t), "*main.FooBar {Baz: 5, Bur: \"strum\"}", hasChildren)
  2401  
  2402  						client.EvaluateRequest("&a7", frame, "repl")
  2403  						pa7 := client.ExpectEvaluateResponse(t)
  2404  						ref = checkEvalRegex(t, pa7, `\*\(\*main\.FooBar\)\(0x[0-9a-f]+\)`, hasChildren)
  2405  						if ref > 0 {
  2406  							client.VariablesRequest(ref)
  2407  							a7 := client.ExpectVariablesResponse(t)
  2408  							checkVarExact(t, a7, 0, "a7", "(*(&a7))", "*main.FooBar {Baz: 5, Bur: \"strum\"}", "*main.FooBar", hasChildren)
  2409  						}
  2410  					}
  2411  
  2412  					// Frame-independent loading expressions allow us to auto-load
  2413  					// variables in any frame, not just topmost.
  2414  					loadvars(1000 /*first topmost frame*/)
  2415  					// step into another function
  2416  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{{Name: "main.barfoo"}})
  2417  					client.ExpectSetFunctionBreakpointsResponse(t)
  2418  					client.ContinueRequest(1)
  2419  					client.ExpectContinueResponse(t)
  2420  					client.ExpectStoppedEvent(t)
  2421  					checkStop(t, client, 1, "main.barfoo", -1)
  2422  					loadvars(1001 /*second frame here is same as topmost above*/)
  2423  				},
  2424  				disconnect: true,
  2425  			}})
  2426  	})
  2427  }
  2428  
  2429  // TestVariablesMetadata exposes test cases where variables contain metadata that
  2430  // can be accessed by requesting named variables.
  2431  func TestVariablesMetadata(t *testing.T) {
  2432  	runTest(t, "testvariables2", func(client *daptest.Client, fixture protest.Fixture) {
  2433  		runDebugSessionWithBPs(t, client, "launch",
  2434  			// Launch
  2435  			func() {
  2436  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  2437  			},
  2438  			// Breakpoints are set within the program
  2439  			fixture.Source, []int{},
  2440  			[]onBreakpoint{{
  2441  				execute:    func() {},
  2442  				disconnect: false,
  2443  			}, {
  2444  				execute: func() {
  2445  					checkStop(t, client, 1, "main.main", -1)
  2446  
  2447  					client.VariablesRequest(localsScope)
  2448  					locals := client.ExpectVariablesResponse(t)
  2449  
  2450  					checkNamedChildren := func(ref int, name, typeStr string, vals []string, evaluate bool) {
  2451  						// byteslice, request named variables
  2452  						client.NamedVariablesRequest(ref)
  2453  						named := client.ExpectVariablesResponse(t)
  2454  						checkChildren(t, named, name, 1)
  2455  						checkVarExact(t, named, 0, "string()", "", "\"tèst\"", "string", false)
  2456  
  2457  						client.VariablesRequest(ref)
  2458  						all := client.ExpectVariablesResponse(t)
  2459  						checkChildren(t, all, name, len(vals)+1)
  2460  						checkVarExact(t, all, 0, "string()", "", "\"tèst\"", "string", false)
  2461  						for i, v := range vals {
  2462  							idx := fmt.Sprintf("[%d]", i)
  2463  							evalName := fmt.Sprintf("%s[%d]", name, i)
  2464  							if evaluate {
  2465  								evalName = fmt.Sprintf("(%s)[%d]", name, i)
  2466  							}
  2467  							checkVarExact(t, all, i+1, idx, evalName, v, typeStr, false)
  2468  						}
  2469  					}
  2470  
  2471  					bytes := []string{"116 = 0x74", "195 = 0xc3", "168 = 0xa8", "115 = 0x73", "116 = 0x74"}
  2472  					runes := []string{"116", "232", "115", "116"}
  2473  
  2474  					// byteslice
  2475  					ref := checkVarExactIndexed(t, locals, -1, "byteslice", "byteslice", "[]uint8 len: 5, cap: 5, [116,195,168,115,116]", "[]uint8", true, 5, 1)
  2476  					checkNamedChildren(ref, "byteslice", "uint8", bytes, false)
  2477  
  2478  					client.EvaluateRequest("byteslice", 0, "")
  2479  					got := client.ExpectEvaluateResponse(t)
  2480  					ref = checkEvalIndexed(t, got, "[]uint8 len: 5, cap: 5, [116,195,168,115,116]", hasChildren, 5, 1)
  2481  					checkNamedChildren(ref, "byteslice", "uint8", bytes, true)
  2482  
  2483  					// runeslice
  2484  					ref = checkVarExactIndexed(t, locals, -1, "runeslice", "runeslice", "[]int32 len: 4, cap: 4, [116,232,115,116]", "[]int32", true, 4, 1)
  2485  					checkNamedChildren(ref, "runeslice", "int32", runes, false)
  2486  
  2487  					client.EvaluateRequest("runeslice", 0, "repl")
  2488  					got = client.ExpectEvaluateResponse(t)
  2489  					ref = checkEvalIndexed(t, got, "[]int32 len: 4, cap: 4, [116,232,115,116]", hasChildren, 4, 1)
  2490  					checkNamedChildren(ref, "runeslice", "int32", runes, true)
  2491  
  2492  					// bytearray
  2493  					ref = checkVarExactIndexed(t, locals, -1, "bytearray", "bytearray", "[5]uint8 [116,195,168,115,116]", "[5]uint8", true, 5, 1)
  2494  					checkNamedChildren(ref, "bytearray", "uint8", bytes, false)
  2495  
  2496  					client.EvaluateRequest("bytearray", 0, "hover")
  2497  					got = client.ExpectEvaluateResponse(t)
  2498  					ref = checkEvalIndexed(t, got, "[5]uint8 [116,195,168,115,116]", hasChildren, 5, 1)
  2499  					checkNamedChildren(ref, "bytearray", "uint8", bytes, true)
  2500  
  2501  					// runearray
  2502  					ref = checkVarExactIndexed(t, locals, -1, "runearray", "runearray", "[4]int32 [116,232,115,116]", "[4]int32", true, 4, 1)
  2503  					checkNamedChildren(ref, "runearray", "int32", runes, false)
  2504  
  2505  					client.EvaluateRequest("runearray", 0, "watch")
  2506  					got = client.ExpectEvaluateResponse(t)
  2507  					ref = checkEvalIndexed(t, got, "[4]int32 [116,232,115,116]", hasChildren, 4, 1)
  2508  					checkNamedChildren(ref, "runearray", "int32", runes, true)
  2509  
  2510  					// string feature is not available with user-defined byte types
  2511  					ref = checkVarExactIndexed(t, locals, -1, "bytestypeslice", "bytestypeslice", "[]main.Byte len: 5, cap: 5, [116,195,168,115,116]", "[]main.Byte", true, 5, 1)
  2512  					client.NamedVariablesRequest(ref)
  2513  					namedchildren := client.ExpectVariablesResponse(t)
  2514  					checkChildren(t, namedchildren, "bytestypeslice as string", 0)
  2515  					ref = checkVarExactIndexed(t, locals, -1, "bytetypearray", "bytetypearray", "[5]main.Byte [116,195,168,115,116]", "[5]main.Byte", true, 5, 1)
  2516  					client.NamedVariablesRequest(ref)
  2517  					namedchildren = client.ExpectVariablesResponse(t)
  2518  					checkChildren(t, namedchildren, "bytetypearray as string", 0)
  2519  				},
  2520  				disconnect: true,
  2521  			}})
  2522  	})
  2523  }
  2524  
  2525  // TestGlobalScopeAndVariables launches the program with showGlobalVariables
  2526  // arg set, executes to a breakpoint in the main package and tests that global
  2527  // package main variables got loaded. It then steps into a function
  2528  // in another package and tests that globals scope got updated to those vars.
  2529  func TestGlobalScopeAndVariables(t *testing.T) {
  2530  	runTest(t, "consts", func(client *daptest.Client, fixture protest.Fixture) {
  2531  		runDebugSessionWithBPs(t, client, "launch",
  2532  			// Launch
  2533  			func() {
  2534  				client.LaunchRequestWithArgs(map[string]interface{}{
  2535  					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true, "showRegisters": true,
  2536  				})
  2537  			},
  2538  			// Breakpoints are set within the program
  2539  			fixture.Source, []int{},
  2540  			[]onBreakpoint{{
  2541  				// Stop at line 36
  2542  				execute: func() {
  2543  					if runtime.GOARCH == "386" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 18) && !goversion.VersionAfterOrEqual(runtime.Version(), 1, 21) {
  2544  						client.StepInRequest(1)
  2545  						client.ExpectStepInResponse(t)
  2546  						client.ExpectStoppedEvent(t)
  2547  					}
  2548  					client.StackTraceRequest(1, 0, 20)
  2549  					stack := client.ExpectStackTraceResponse(t)
  2550  					checkStackFramesExact(t, stack, "main.main", 36, 1000, 3, 3)
  2551  
  2552  					client.ScopesRequest(1000)
  2553  					scopes := client.ExpectScopesResponse(t)
  2554  					checkScope(t, scopes, 0, "Locals", localsScope)
  2555  					checkScope(t, scopes, 1, "Globals (package main)", globalsScope)
  2556  					checkScope(t, scopes, 2, "Registers", globalsScope+1)
  2557  
  2558  					client.VariablesRequest(globalsScope)
  2559  					client.ExpectVariablesResponse(t)
  2560  					// The program has no user-defined globals.
  2561  					// Depending on the Go version, there might
  2562  					// be some runtime globals (e.g. main..inittask)
  2563  					// so testing for the total number is too fragile.
  2564  
  2565  					// Step into pkg.AnotherMethod()
  2566  					client.StepInRequest(1)
  2567  					client.ExpectStepInResponse(t)
  2568  					client.ExpectStoppedEvent(t)
  2569  
  2570  					client.StackTraceRequest(1, 0, 20)
  2571  					stack = client.ExpectStackTraceResponse(t)
  2572  					checkStackFramesExact(t, stack, "", 13, 1000, 4, 4)
  2573  
  2574  					client.ScopesRequest(1000)
  2575  					scopes = client.ExpectScopesResponse(t)
  2576  					checkScope(t, scopes, 0, "Locals", localsScope)
  2577  					checkScope(t, scopes, 1, "Globals (package gitlab.com/Raven-IO/raven-delve/_fixtures/internal/dir0/pkg)", globalsScope)
  2578  
  2579  					client.VariablesRequest(globalsScope)
  2580  					globals := client.ExpectVariablesResponse(t)
  2581  					checkChildren(t, globals, "Globals", 1)
  2582  					ref := checkVarExact(t, globals, 0, "SomeVar", "gitlab.com/Raven-IO/raven-delve/_fixtures/internal/dir0/pkg.SomeVar", "pkg.SomeType {X: 0}", "gitlab.com/Raven-IO/raven-delve/_fixtures/internal/dir0/pkg.SomeType", hasChildren)
  2583  
  2584  					if ref > 0 {
  2585  						client.VariablesRequest(ref)
  2586  						somevar := client.ExpectVariablesResponse(t)
  2587  						checkChildren(t, somevar, "SomeVar", 1)
  2588  						// TODO(polina): unlike main.p, this prefix won't work
  2589  						checkVarExact(t, somevar, 0, "X", "gitlab.com/Raven-IO/raven-delve/_fixtures/internal/dir0/pkg.SomeVar.X", "0", "float64", noChildren)
  2590  					}
  2591  				},
  2592  				disconnect: false,
  2593  			}})
  2594  	})
  2595  }
  2596  
  2597  // TestRegisterScopeAndVariables launches the program with showRegisters
  2598  // arg set, executes to a breakpoint in the main package and tests that the registers
  2599  // got loaded. It then steps into a function in another package and tests that
  2600  // the registers were updated by checking PC.
  2601  func TestRegistersScopeAndVariables(t *testing.T) {
  2602  	if runtime.GOARCH == "ppc64le" {
  2603  		t.Skip("skipped on ppc64le: broken")
  2604  	}
  2605  	runTest(t, "consts", func(client *daptest.Client, fixture protest.Fixture) {
  2606  		runDebugSessionWithBPs(t, client, "launch",
  2607  			// Launch
  2608  			func() {
  2609  				client.LaunchRequestWithArgs(map[string]interface{}{
  2610  					"mode": "exec", "program": fixture.Path, "showRegisters": true,
  2611  				})
  2612  			},
  2613  			// Breakpoints are set within the program
  2614  			fixture.Source, []int{},
  2615  			[]onBreakpoint{{
  2616  				// Stop at line 36
  2617  				execute: func() {
  2618  					if runtime.GOARCH == "386" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 18) && !goversion.VersionAfterOrEqual(runtime.Version(), 1, 21) {
  2619  						client.StepInRequest(1)
  2620  						client.ExpectStepInResponse(t)
  2621  						client.ExpectStoppedEvent(t)
  2622  					}
  2623  
  2624  					client.StackTraceRequest(1, 0, 20)
  2625  					stack := client.ExpectStackTraceResponse(t)
  2626  					checkStackFramesExact(t, stack, "main.main", 36, 1000, 3, 3)
  2627  
  2628  					client.ScopesRequest(1000)
  2629  					scopes := client.ExpectScopesResponse(t)
  2630  					checkScope(t, scopes, 0, "Locals", localsScope)
  2631  					registersScope := localsScope + 1
  2632  					checkScope(t, scopes, 1, "Registers", registersScope)
  2633  
  2634  					// Check that instructionPointer points to the InstructionPointerReference.
  2635  					pc, err := getPC(t, client, 1)
  2636  					if err != nil {
  2637  						t.Error(pc)
  2638  					}
  2639  
  2640  					client.VariablesRequest(registersScope)
  2641  					vr := client.ExpectVariablesResponse(t)
  2642  					if len(vr.Body.Variables) == 0 {
  2643  						t.Fatal("no registers returned")
  2644  					}
  2645  					idx := findPcReg(vr.Body.Variables)
  2646  					if idx < 0 {
  2647  						t.Fatalf("got %#v, want a reg with instruction pointer", vr.Body.Variables)
  2648  					}
  2649  					pcReg := vr.Body.Variables[idx]
  2650  					gotPc, err := strconv.ParseUint(pcReg.Value, 0, 64)
  2651  					if err != nil {
  2652  						t.Error(err)
  2653  					}
  2654  					name := strings.TrimSpace(pcReg.Name)
  2655  					if gotPc != pc || pcReg.EvaluateName != fmt.Sprintf("_%s", strings.ToUpper(name)) {
  2656  						t.Errorf("got %#v,\nwant Name=%s Value=%#x EvaluateName=%q", pcReg, name, pc, fmt.Sprintf("_%s", strings.ToUpper(name)))
  2657  					}
  2658  
  2659  					// The program has no user-defined globals.
  2660  					// Depending on the Go version, there might
  2661  					// be some runtime globals (e.g. main..inittask)
  2662  					// so testing for the total number is too fragile.
  2663  
  2664  					// Step into pkg.AnotherMethod()
  2665  					client.StepInRequest(1)
  2666  					client.ExpectStepInResponse(t)
  2667  					client.ExpectStoppedEvent(t)
  2668  
  2669  					client.StackTraceRequest(1, 0, 20)
  2670  					stack = client.ExpectStackTraceResponse(t)
  2671  					checkStackFramesExact(t, stack, "", 13, 1000, 4, 4)
  2672  
  2673  					client.ScopesRequest(1000)
  2674  					scopes = client.ExpectScopesResponse(t)
  2675  					checkScope(t, scopes, 0, "Locals", localsScope)
  2676  					checkScope(t, scopes, 1, "Registers", registersScope)
  2677  
  2678  					// Check that rip points to the InstructionPointerReference.
  2679  					pc, err = getPC(t, client, 1)
  2680  					if err != nil {
  2681  						t.Error(pc)
  2682  					}
  2683  					client.VariablesRequest(registersScope)
  2684  					vr = client.ExpectVariablesResponse(t)
  2685  					if len(vr.Body.Variables) == 0 {
  2686  						t.Fatal("no registers returned")
  2687  					}
  2688  
  2689  					idx = findPcReg(vr.Body.Variables)
  2690  					if idx < 0 {
  2691  						t.Fatalf("got %#v, want a reg with instruction pointer", vr.Body.Variables)
  2692  					}
  2693  					pcReg = vr.Body.Variables[idx]
  2694  					gotPc, err = strconv.ParseUint(pcReg.Value, 0, 64)
  2695  					if err != nil {
  2696  						t.Error(err)
  2697  					}
  2698  
  2699  					if gotPc != pc || pcReg.EvaluateName != fmt.Sprintf("_%s", strings.ToUpper(name)) {
  2700  						t.Errorf("got %#v,\nwant Name=%s Value=%#x EvaluateName=%q", pcReg, name, pc, fmt.Sprintf("_%s", strings.ToUpper(name)))
  2701  					}
  2702  				},
  2703  				disconnect: false,
  2704  			}})
  2705  	})
  2706  }
  2707  
  2708  func findPcReg(regs []dap.Variable) int {
  2709  	for i, reg := range regs {
  2710  		if isPcReg(reg) {
  2711  			return i
  2712  		}
  2713  	}
  2714  	return -1
  2715  }
  2716  
  2717  func isPcReg(reg dap.Variable) bool {
  2718  	pcRegNames := []string{"rip", "pc", "eip"}
  2719  	for _, name := range pcRegNames {
  2720  		if name == strings.TrimSpace(reg.Name) {
  2721  			return true
  2722  		}
  2723  	}
  2724  	return false
  2725  }
  2726  
  2727  // TestShadowedVariables executes to a breakpoint and checks the shadowed
  2728  // variable is named correctly.
  2729  func TestShadowedVariables(t *testing.T) {
  2730  	runTest(t, "testshadow", func(client *daptest.Client, fixture protest.Fixture) {
  2731  		runDebugSessionWithBPs(t, client, "launch",
  2732  			// Launch
  2733  			func() {
  2734  				client.LaunchRequestWithArgs(map[string]interface{}{
  2735  					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,
  2736  				})
  2737  			},
  2738  			// Breakpoints are set within the program
  2739  			fixture.Source, []int{},
  2740  			[]onBreakpoint{{
  2741  				// Stop at line 13
  2742  				execute: func() {
  2743  					client.StackTraceRequest(1, 0, 20)
  2744  					stack := client.ExpectStackTraceResponse(t)
  2745  					checkStackFramesExact(t, stack, "main.main", 13, 1000, 3, 3)
  2746  
  2747  					client.ScopesRequest(1000)
  2748  					scopes := client.ExpectScopesResponse(t)
  2749  					checkScope(t, scopes, 0, "Locals", localsScope)
  2750  					checkScope(t, scopes, 1, "Globals (package main)", globalsScope)
  2751  
  2752  					client.VariablesRequest(localsScope)
  2753  					locals := client.ExpectVariablesResponse(t)
  2754  
  2755  					checkVarExact(t, locals, 0, "(a)", "a", "0", "int", !hasChildren)
  2756  					checkVarExact(t, locals, 1, "a", "a", "1", "int", !hasChildren)
  2757  
  2758  					// Check that the non-shadowed of "a" is returned from evaluate request.
  2759  					validateEvaluateName(t, client, locals, 1)
  2760  				},
  2761  				disconnect: false,
  2762  			}})
  2763  	})
  2764  }
  2765  
  2766  // Tests that 'stackTraceDepth' from LaunchRequest is parsed and passed to
  2767  // stacktrace requests handlers.
  2768  func TestLaunchRequestWithStackTraceDepth(t *testing.T) {
  2769  	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
  2770  		var stResp *dap.StackTraceResponse
  2771  		runDebugSessionWithBPs(t, client, "launch",
  2772  			// Launch
  2773  			func() {
  2774  				client.LaunchRequestWithArgs(map[string]interface{}{
  2775  					"mode": "exec", "program": fixture.Path, "stackTraceDepth": 1,
  2776  				})
  2777  			},
  2778  			// Set breakpoints
  2779  			fixture.Source, []int{8},
  2780  			[]onBreakpoint{{ // Stop at line 8
  2781  				execute: func() {
  2782  					client.StackTraceRequest(1, 0, 0)
  2783  					stResp = client.ExpectStackTraceResponse(t)
  2784  					checkStackFramesHasMore(t, stResp, "main.Increment", 8, 1000, 1 /*returned*/, 2 /*available*/)
  2785  				},
  2786  				disconnect: false,
  2787  			}})
  2788  	})
  2789  }
  2790  
  2791  type Breakpoint struct {
  2792  	line      int
  2793  	path      string
  2794  	verified  bool
  2795  	msgPrefix string
  2796  }
  2797  
  2798  func expectSetBreakpointsResponse(t *testing.T, client *daptest.Client, bps []Breakpoint) {
  2799  	t.Helper()
  2800  	checkSetBreakpointsResponse(t, bps, client.ExpectSetBreakpointsResponse(t))
  2801  }
  2802  
  2803  func checkSetBreakpointsResponse(t *testing.T, bps []Breakpoint, got *dap.SetBreakpointsResponse) {
  2804  	t.Helper()
  2805  	checkBreakpoints(t, bps, got.Body.Breakpoints)
  2806  }
  2807  
  2808  func checkBreakpoints(t *testing.T, bps []Breakpoint, breakpoints []dap.Breakpoint) {
  2809  	t.Helper()
  2810  	if len(breakpoints) != len(bps) {
  2811  		t.Errorf("got %#v,\nwant len(Breakpoints)=%d", breakpoints, len(bps))
  2812  		return
  2813  	}
  2814  	for i, bp := range breakpoints {
  2815  		if bps[i].line < 0 && !bps[i].verified {
  2816  			if bp.Verified != bps[i].verified || !stringContainsCaseInsensitive(bp.Message, bps[i].msgPrefix) {
  2817  				t.Errorf("got breakpoints[%d] = %#v, \nwant %#v", i, bp, bps[i])
  2818  			}
  2819  			continue
  2820  		}
  2821  		if bp.Line != bps[i].line || bp.Verified != bps[i].verified || bp.Source.Path != bps[i].path ||
  2822  			!strings.HasPrefix(bp.Message, bps[i].msgPrefix) {
  2823  			t.Errorf("got breakpoints[%d] = %#v, \nwant %#v", i, bp, bps[i])
  2824  		}
  2825  	}
  2826  }
  2827  
  2828  // TestSetBreakpoint executes to a breakpoint and tests different
  2829  // configurations of setBreakpoint requests.
  2830  func TestSetBreakpoint(t *testing.T) {
  2831  	runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
  2832  		runDebugSessionWithBPs(t, client, "launch",
  2833  			// Launch
  2834  			func() {
  2835  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  2836  			},
  2837  			// Set breakpoints
  2838  			fixture.Source, []int{16}, // b main.main
  2839  			[]onBreakpoint{{
  2840  				execute: func() {
  2841  					checkStop(t, client, 1, "main.main", 16)
  2842  
  2843  					// Set two breakpoints at the next two lines in main
  2844  					client.SetBreakpointsRequest(fixture.Source, []int{17, 18})
  2845  					expectSetBreakpointsResponse(t, client, []Breakpoint{{17, fixture.Source, true, ""}, {18, fixture.Source, true, ""}})
  2846  
  2847  					// Clear 17, reset 18
  2848  					client.SetBreakpointsRequest(fixture.Source, []int{18})
  2849  					expectSetBreakpointsResponse(t, client, []Breakpoint{{18, fixture.Source, true, ""}})
  2850  
  2851  					// Skip 17, continue to 18
  2852  					client.ContinueRequest(1)
  2853  					client.ExpectContinueResponse(t)
  2854  					client.ExpectStoppedEvent(t)
  2855  					checkStop(t, client, 1, "main.main", 18)
  2856  
  2857  					// Set another breakpoint inside the loop in loop(), twice to trigger error
  2858  					client.SetBreakpointsRequest(fixture.Source, []int{8, 8})
  2859  					expectSetBreakpointsResponse(t, client, []Breakpoint{{8, fixture.Source, true, ""}, {-1, "", false, "breakpoint already exists"}})
  2860  
  2861  					// Continue into the loop
  2862  					client.ContinueRequest(1)
  2863  					client.ExpectContinueResponse(t)
  2864  					client.ExpectStoppedEvent(t)
  2865  					checkStop(t, client, 1, "main.loop", 8)
  2866  					client.VariablesRequest(localsScope)
  2867  					locals := client.ExpectVariablesResponse(t)
  2868  					checkVarExact(t, locals, 0, "i", "i", "0", "int", noChildren) // i == 0
  2869  
  2870  					// Edit the breakpoint to add a condition
  2871  					client.SetBreakpointsRequestWithArgs(fixture.Source, []int{8}, map[int]string{8: "i == 3"}, nil, nil)
  2872  					expectSetBreakpointsResponse(t, client, []Breakpoint{{8, fixture.Source, true, ""}})
  2873  
  2874  					// Continue until condition is hit
  2875  					client.ContinueRequest(1)
  2876  					client.ExpectContinueResponse(t)
  2877  					client.ExpectStoppedEvent(t)
  2878  					checkStop(t, client, 1, "main.loop", 8)
  2879  					client.VariablesRequest(localsScope)
  2880  					locals = client.ExpectVariablesResponse(t)
  2881  					checkVarExact(t, locals, 0, "i", "i", "3", "int", noChildren) // i == 3
  2882  
  2883  					// Edit the breakpoint to remove a condition
  2884  					client.SetBreakpointsRequestWithArgs(fixture.Source, []int{8}, map[int]string{8: ""}, nil, nil)
  2885  					expectSetBreakpointsResponse(t, client, []Breakpoint{{8, fixture.Source, true, ""}})
  2886  
  2887  					// Continue for one more loop iteration
  2888  					client.ContinueRequest(1)
  2889  					client.ExpectContinueResponse(t)
  2890  					client.ExpectStoppedEvent(t)
  2891  					checkStop(t, client, 1, "main.loop", 8)
  2892  					client.VariablesRequest(localsScope)
  2893  					locals = client.ExpectVariablesResponse(t)
  2894  					checkVarExact(t, locals, 0, "i", "i", "4", "int", noChildren) // i == 4
  2895  
  2896  					// Set at a line without a statement
  2897  					client.SetBreakpointsRequest(fixture.Source, []int{1000})
  2898  					expectSetBreakpointsResponse(t, client, []Breakpoint{{-1, "", false, "could not find statement"}}) // all cleared, none set
  2899  				},
  2900  				// The program has an infinite loop, so we must kill it by disconnecting.
  2901  				disconnect: true,
  2902  			}})
  2903  	})
  2904  }
  2905  
  2906  // TestSetInstructionBreakpoint executes to a breakpoint and tests different
  2907  // configurations of setInstructionBreakpoint requests.
  2908  func TestSetInstructionBreakpoint(t *testing.T) {
  2909  	runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
  2910  		runDebugSessionWithBPs(t, client, "launch",
  2911  			// Launch
  2912  			func() {
  2913  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  2914  			},
  2915  			// Set breakpoints
  2916  			fixture.Source, []int{16}, // b main.main
  2917  			[]onBreakpoint{{
  2918  				execute: func() {
  2919  					checkStop(t, client, 1, "main.main", 16)
  2920  
  2921  					// Set two breakpoints in the loop
  2922  					client.SetBreakpointsRequest(fixture.Source, []int{8, 9})
  2923  					expectSetBreakpointsResponse(t, client, []Breakpoint{{8, fixture.Source, true, ""}, {9, fixture.Source, true, ""}})
  2924  
  2925  					// Continue to the two breakpoints and get the instructionPointerReference.
  2926  					client.ContinueRequest(1)
  2927  					client.ExpectContinueResponse(t)
  2928  					client.ExpectStoppedEvent(t)
  2929  					checkStop(t, client, 1, "main.loop", 8)
  2930  
  2931  					client.StackTraceRequest(1, 0, 1)
  2932  					st := client.ExpectStackTraceResponse(t)
  2933  					if len(st.Body.StackFrames) < 1 {
  2934  						t.Fatalf("\ngot  %#v\nwant len(stackframes) => 1", st)
  2935  					}
  2936  					pc8 := st.Body.StackFrames[0].InstructionPointerReference
  2937  
  2938  					client.ContinueRequest(1)
  2939  					client.ExpectContinueResponse(t)
  2940  					client.ExpectStoppedEvent(t)
  2941  					checkStop(t, client, 1, "main.loop", 9)
  2942  
  2943  					client.StackTraceRequest(1, 0, 1)
  2944  					st = client.ExpectStackTraceResponse(t)
  2945  					if len(st.Body.StackFrames) < 1 {
  2946  						t.Fatalf("\ngot  %#v\nwant len(stackframes) => 1", st)
  2947  					}
  2948  					pc9 := st.Body.StackFrames[0].InstructionPointerReference
  2949  
  2950  					// Clear the source breakpoints.
  2951  					// TODO(suzmue): there is an existing issue that breakpoints with identical locations
  2952  					// from different setBreakpoints, setFunctionBreakpoints, setInstructionBreakpoints
  2953  					// requests will prevent subsequent ones from being set.
  2954  					client.SetBreakpointsRequest(fixture.Source, []int{})
  2955  					expectSetBreakpointsResponse(t, client, []Breakpoint{})
  2956  
  2957  					// Set the breakpoints using the instruction pointer references.
  2958  					client.SetInstructionBreakpointsRequest([]dap.InstructionBreakpoint{{InstructionReference: pc8}, {InstructionReference: pc9}})
  2959  					bps := client.ExpectSetInstructionBreakpointsResponse(t).Body.Breakpoints
  2960  					checkBreakpoints(t, []Breakpoint{{line: 8, path: fixture.Source, verified: true}, {line: 9, path: fixture.Source, verified: true}}, bps)
  2961  
  2962  					// Continue to the two breakpoints and get the instructionPointerReference.
  2963  					client.ContinueRequest(1)
  2964  					client.ExpectContinueResponse(t)
  2965  					client.ExpectStoppedEvent(t)
  2966  					checkStop(t, client, 1, "main.loop", 8)
  2967  
  2968  					client.ContinueRequest(1)
  2969  					client.ExpectContinueResponse(t)
  2970  					client.ExpectStoppedEvent(t)
  2971  					checkStop(t, client, 1, "main.loop", 9)
  2972  
  2973  					// Remove the breakpoint on line 8 and continue.
  2974  					client.SetInstructionBreakpointsRequest([]dap.InstructionBreakpoint{{InstructionReference: pc9}})
  2975  					bps = client.ExpectSetInstructionBreakpointsResponse(t).Body.Breakpoints
  2976  					checkBreakpoints(t, []Breakpoint{{line: 9, path: fixture.Source, verified: true}}, bps)
  2977  
  2978  					client.ContinueRequest(1)
  2979  					client.ExpectContinueResponse(t)
  2980  					client.ExpectStoppedEvent(t)
  2981  					checkStop(t, client, 1, "main.loop", 9)
  2982  
  2983  					// Set two breakpoints and expect an error on the second one.
  2984  					client.SetInstructionBreakpointsRequest([]dap.InstructionBreakpoint{{InstructionReference: pc8}, {InstructionReference: pc8}})
  2985  					bps = client.ExpectSetInstructionBreakpointsResponse(t).Body.Breakpoints
  2986  					checkBreakpoints(t, []Breakpoint{{line: 8, path: fixture.Source, verified: true}, {line: -1, path: "", verified: false, msgPrefix: "breakpoint already exists"}}, bps)
  2987  
  2988  					// Add a condition
  2989  					client.SetInstructionBreakpointsRequest([]dap.InstructionBreakpoint{{InstructionReference: pc8, Condition: "i == 100"}})
  2990  					bps = client.ExpectSetInstructionBreakpointsResponse(t).Body.Breakpoints
  2991  					checkBreakpoints(t, []Breakpoint{{line: 8, path: fixture.Source, verified: true}}, bps)
  2992  
  2993  					client.ContinueRequest(1)
  2994  					client.ExpectContinueResponse(t)
  2995  					client.ExpectStoppedEvent(t)
  2996  					checkStop(t, client, 1, "main.loop", 8)
  2997  
  2998  					client.VariablesRequest(localsScope)
  2999  					locals := client.ExpectVariablesResponse(t)
  3000  					checkVarExact(t, locals, 0, "i", "i", "100", "int", noChildren) // i == 100
  3001  				},
  3002  				// The program has an infinite loop, so we must kill it by disconnecting.
  3003  				disconnect: true,
  3004  			}})
  3005  	})
  3006  }
  3007  
  3008  func TestPauseAtStop(t *testing.T) {
  3009  	runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
  3010  		runDebugSessionWithBPs(t, client, "launch",
  3011  			// Launch
  3012  			func() {
  3013  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  3014  			},
  3015  			// Set breakpoints
  3016  			fixture.Source, []int{16},
  3017  			[]onBreakpoint{{
  3018  				execute: func() {
  3019  					checkStop(t, client, 1, "main.main", 16)
  3020  
  3021  					client.SetBreakpointsRequest(fixture.Source, []int{6, 8})
  3022  					expectSetBreakpointsResponse(t, client, []Breakpoint{{6, fixture.Source, true, ""}, {8, fixture.Source, true, ""}})
  3023  
  3024  					// Send a pause request while stopped on a cleared breakpoint.
  3025  					client.PauseRequest(1)
  3026  					client.ExpectPauseResponse(t)
  3027  
  3028  					client.ContinueRequest(1)
  3029  					client.ExpectContinueResponse(t)
  3030  					client.ExpectStoppedEvent(t)
  3031  					checkStop(t, client, 1, "main.loop", 6)
  3032  
  3033  					// Send a pause request while stopped on a breakpoint.
  3034  					client.PauseRequest(1)
  3035  					client.ExpectPauseResponse(t)
  3036  
  3037  					client.ContinueRequest(1)
  3038  					client.ExpectContinueResponse(t)
  3039  					se := client.ExpectStoppedEvent(t)
  3040  					if se.Body.Reason != "breakpoint" {
  3041  						t.Errorf("got %#v, expected breakpoint", se)
  3042  					}
  3043  					checkStop(t, client, 1, "main.loop", 8)
  3044  
  3045  					// Send a pause request while stopped after stepping.
  3046  					client.NextRequest(1)
  3047  					client.ExpectNextResponse(t)
  3048  					client.ExpectStoppedEvent(t)
  3049  					checkStop(t, client, 1, "main.loop", 9)
  3050  
  3051  					client.PauseRequest(1)
  3052  					client.ExpectPauseResponse(t)
  3053  
  3054  					client.ContinueRequest(1)
  3055  					client.ExpectContinueResponse(t)
  3056  
  3057  					client.ExpectStoppedEvent(t)
  3058  					checkStop(t, client, 1, "main.loop", 8)
  3059  				},
  3060  				// The program has an infinite loop, so we must kill it by disconnecting.
  3061  				disconnect: true,
  3062  			}})
  3063  	})
  3064  }
  3065  
  3066  func checkHitBreakpointIds(t *testing.T, se *dap.StoppedEvent, reason string, id int) {
  3067  	if se.Body.ThreadId != 1 || se.Body.Reason != reason || len(se.Body.HitBreakpointIds) != 1 || se.Body.HitBreakpointIds[0] != id {
  3068  		t.Errorf("got %#v, want Reason=%q, ThreadId=1, HitBreakpointIds=[]int{%d}", se, reason, id)
  3069  	}
  3070  }
  3071  
  3072  // TestHitBreakpointIds executes to a breakpoint and tests that
  3073  // the breakpoint ids in the stopped event are correct.
  3074  func TestHitBreakpointIds(t *testing.T) {
  3075  	runTest(t, "locationsprog", func(client *daptest.Client, fixture protest.Fixture) {
  3076  		runDebugSessionWithBPs(t, client, "launch",
  3077  			// Launch
  3078  			func() {
  3079  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  3080  			},
  3081  			// Set breakpoints
  3082  			fixture.Source, []int{30}, // b main.main
  3083  			[]onBreakpoint{{
  3084  				execute: func() {
  3085  					checkStop(t, client, 1, "main.main", 30)
  3086  
  3087  					// Set two source breakpoints and two function breakpoints.
  3088  					client.SetBreakpointsRequest(fixture.Source, []int{23, 33})
  3089  					sourceBps := client.ExpectSetBreakpointsResponse(t).Body.Breakpoints
  3090  					checkBreakpoints(t, []Breakpoint{{line: 23, path: fixture.Source, verified: true}, {line: 33, path: fixture.Source, verified: true}}, sourceBps)
  3091  
  3092  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3093  						{Name: "anotherFunction"},
  3094  						{Name: "anotherFunction:1"},
  3095  					})
  3096  					functionBps := client.ExpectSetFunctionBreakpointsResponse(t).Body.Breakpoints
  3097  					checkBreakpoints(t, []Breakpoint{{line: 26, path: fixture.Source, verified: true}, {line: 27, path: fixture.Source, verified: true}}, functionBps)
  3098  
  3099  					client.ContinueRequest(1)
  3100  					client.ExpectContinueResponse(t)
  3101  					se := client.ExpectStoppedEvent(t)
  3102  					checkHitBreakpointIds(t, se, "breakpoint", sourceBps[1].Id)
  3103  					checkStop(t, client, 1, "main.main", 33)
  3104  
  3105  					client.ContinueRequest(1)
  3106  					client.ExpectContinueResponse(t)
  3107  					se = client.ExpectStoppedEvent(t)
  3108  					checkHitBreakpointIds(t, se, "breakpoint", sourceBps[0].Id)
  3109  					checkStop(t, client, 1, "main.(*SomeType).SomeFunction", 23)
  3110  
  3111  					client.ContinueRequest(1)
  3112  					client.ExpectContinueResponse(t)
  3113  					se = client.ExpectStoppedEvent(t)
  3114  					checkHitBreakpointIds(t, se, "function breakpoint", functionBps[0].Id)
  3115  					checkStop(t, client, 1, "main.anotherFunction", 26)
  3116  
  3117  					client.ContinueRequest(1)
  3118  					client.ExpectContinueResponse(t)
  3119  					se = client.ExpectStoppedEvent(t)
  3120  
  3121  					checkHitBreakpointIds(t, se, "function breakpoint", functionBps[1].Id)
  3122  
  3123  					checkStop(t, client, 1, "main.anotherFunction", 27)
  3124  				},
  3125  				disconnect: true,
  3126  			}})
  3127  	})
  3128  }
  3129  
  3130  func stringContainsCaseInsensitive(got, want string) bool {
  3131  	return strings.Contains(strings.ToLower(got), strings.ToLower(want))
  3132  }
  3133  
  3134  // TestSetFunctionBreakpoints is inspired by service/test.TestClientServer_FindLocations.
  3135  func TestSetFunctionBreakpoints(t *testing.T) {
  3136  	runTest(t, "locationsprog", func(client *daptest.Client, fixture protest.Fixture) {
  3137  		runDebugSessionWithBPs(t, client, "launch",
  3138  			// Launch
  3139  			func() {
  3140  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  3141  			},
  3142  			// Set breakpoints
  3143  			fixture.Source, []int{30}, // b main.main
  3144  			[]onBreakpoint{{
  3145  				execute: func() {
  3146  					checkStop(t, client, 1, "main.main", 30)
  3147  
  3148  					type Breakpoint struct {
  3149  						line       int
  3150  						sourceName string
  3151  						verified   bool
  3152  						errMsg     string
  3153  					}
  3154  					expectSetFunctionBreakpointsResponse := func(bps []Breakpoint) {
  3155  						t.Helper()
  3156  						got := client.ExpectSetFunctionBreakpointsResponse(t)
  3157  						if len(got.Body.Breakpoints) != len(bps) {
  3158  							t.Errorf("got %#v,\nwant len(Breakpoints)=%d", got, len(bps))
  3159  							return
  3160  						}
  3161  						for i, bp := range got.Body.Breakpoints {
  3162  							if bps[i].line < 0 && !bps[i].verified {
  3163  								if bp.Verified != bps[i].verified || !stringContainsCaseInsensitive(bp.Message, bps[i].errMsg) {
  3164  									t.Errorf("got breakpoints[%d] = %#v, \nwant %#v", i, bp, bps[i])
  3165  								}
  3166  								continue
  3167  							}
  3168  							// Some function breakpoints may be in packages that have been imported and we do not control, so
  3169  							// we do not always want to check breakpoint lines.
  3170  							if (bps[i].line >= 0 && bp.Line != bps[i].line) || bp.Verified != bps[i].verified || bp.Source.Name != bps[i].sourceName {
  3171  								t.Errorf("got breakpoints[%d] = %#v, \nwant %#v", i, bp, bps[i])
  3172  							}
  3173  						}
  3174  					}
  3175  
  3176  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3177  						{Name: "anotherFunction"},
  3178  					})
  3179  					expectSetFunctionBreakpointsResponse([]Breakpoint{{26, filepath.Base(fixture.Source), true, ""}})
  3180  
  3181  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3182  						{Name: "main.anotherFunction"},
  3183  					})
  3184  					expectSetFunctionBreakpointsResponse([]Breakpoint{{26, filepath.Base(fixture.Source), true, ""}})
  3185  
  3186  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3187  						{Name: "SomeType.String"},
  3188  					})
  3189  					expectSetFunctionBreakpointsResponse([]Breakpoint{{14, filepath.Base(fixture.Source), true, ""}})
  3190  
  3191  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3192  						{Name: "(*SomeType).String"},
  3193  					})
  3194  					expectSetFunctionBreakpointsResponse([]Breakpoint{{14, filepath.Base(fixture.Source), true, ""}})
  3195  
  3196  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3197  						{Name: "main.SomeType.String"},
  3198  					})
  3199  					expectSetFunctionBreakpointsResponse([]Breakpoint{{14, filepath.Base(fixture.Source), true, ""}})
  3200  
  3201  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3202  						{Name: "main.(*SomeType).String"},
  3203  					})
  3204  					expectSetFunctionBreakpointsResponse([]Breakpoint{{14, filepath.Base(fixture.Source), true, ""}})
  3205  
  3206  					// Test line offsets
  3207  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3208  						{Name: "main.anotherFunction:1"},
  3209  					})
  3210  					expectSetFunctionBreakpointsResponse([]Breakpoint{{27, filepath.Base(fixture.Source), true, ""}})
  3211  
  3212  					// Test function names in imported package.
  3213  					// Issue #275
  3214  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3215  						{Name: "io/ioutil.ReadFile"},
  3216  					})
  3217  					expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, "ioutil.go", true, ""}})
  3218  
  3219  					// Issue #296
  3220  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3221  						{Name: "/io/ioutil.ReadFile"},
  3222  					})
  3223  					expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, "ioutil.go", true, ""}})
  3224  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3225  						{Name: "ioutil.ReadFile"},
  3226  					})
  3227  					expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, "ioutil.go", true, ""}})
  3228  
  3229  					// Function Breakpoint name also accepts breakpoints that are specified as file:line.
  3230  					// TODO(suzmue): We could return an error, but it probably is not necessary since breakpoints,
  3231  					// and function breakpoints come in with different requests.
  3232  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3233  						{Name: fmt.Sprintf("%s:14", fixture.Source)},
  3234  					})
  3235  					expectSetFunctionBreakpointsResponse([]Breakpoint{{14, filepath.Base(fixture.Source), true, ""}})
  3236  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3237  						{Name: fmt.Sprintf("%s:14", filepath.Base(fixture.Source))},
  3238  					})
  3239  					expectSetFunctionBreakpointsResponse([]Breakpoint{{14, filepath.Base(fixture.Source), true, ""}})
  3240  
  3241  					// Expect error for ambiguous function name.
  3242  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3243  						{Name: "String"},
  3244  					})
  3245  					expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, "", false, "Location \"String\" ambiguous"}})
  3246  
  3247  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3248  						{Name: "main.String"},
  3249  					})
  3250  					expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, "", false, "Location \"main.String\" ambiguous"}})
  3251  
  3252  					// Expect error for function that does not exist.
  3253  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3254  						{Name: "fakeFunction"},
  3255  					})
  3256  					expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, "", false, "location \"fakeFunction\" not found"}})
  3257  
  3258  					// Expect error for negative line number.
  3259  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3260  						{Name: "main.anotherFunction:-1"},
  3261  					})
  3262  					expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, "", false, "line offset negative or not a number"}})
  3263  
  3264  					// Expect error when function name is regex.
  3265  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3266  						{Name: `/^.*String.*$/`},
  3267  					})
  3268  					expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, "", false, "breakpoint name \"/^.*String.*$/\" could not be parsed as a function"}})
  3269  
  3270  					// Expect error when function name is an offset.
  3271  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3272  						{Name: "+1"},
  3273  					})
  3274  					expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, filepath.Base(fixture.Source), false, "breakpoint name \"+1\" could not be parsed as a function"}})
  3275  
  3276  					// Expect error when function name is a line number.
  3277  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3278  						{Name: "14"},
  3279  					})
  3280  					expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, filepath.Base(fixture.Source), false, "breakpoint name \"14\" could not be parsed as a function"}})
  3281  
  3282  					// Expect error when function name is an address.
  3283  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3284  						{Name: "*b"},
  3285  					})
  3286  					expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, filepath.Base(fixture.Source), false, "breakpoint name \"*b\" could not be parsed as a function"}})
  3287  
  3288  					// Expect error when function name is a relative path.
  3289  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3290  						{Name: fmt.Sprintf(".%s%s:14", string(filepath.Separator), filepath.Base(fixture.Source))},
  3291  					})
  3292  					// This relative path could also be caught by the parser, so we should not match the error message.
  3293  					expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, filepath.Base(fixture.Source), false, ""}})
  3294  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3295  						{Name: fmt.Sprintf("..%s%s:14", string(filepath.Separator), filepath.Base(fixture.Source))},
  3296  					})
  3297  					// This relative path could also be caught by the parser, so we should not match the error message.
  3298  					expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, filepath.Base(fixture.Source), false, ""}})
  3299  
  3300  					// Test multiple function breakpoints.
  3301  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3302  						{Name: "SomeType.String"}, {Name: "anotherFunction"},
  3303  					})
  3304  					expectSetFunctionBreakpointsResponse([]Breakpoint{{14, filepath.Base(fixture.Source), true, ""}, {26, filepath.Base(fixture.Source), true, ""}})
  3305  
  3306  					// Test multiple breakpoints to the same location.
  3307  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3308  						{Name: "SomeType.String"}, {Name: "(*SomeType).String"},
  3309  					})
  3310  					expectSetFunctionBreakpointsResponse([]Breakpoint{{14, filepath.Base(fixture.Source), true, ""}, {-1, "", false, "breakpoint exists"}})
  3311  
  3312  					// Set two breakpoints at SomeType.String and SomeType.SomeFunction.
  3313  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3314  						{Name: "SomeType.String"}, {Name: "SomeType.SomeFunction"},
  3315  					})
  3316  					expectSetFunctionBreakpointsResponse([]Breakpoint{{14, filepath.Base(fixture.Source), true, ""}, {22, filepath.Base(fixture.Source), true, ""}})
  3317  
  3318  					// Clear SomeType.String, reset SomeType.SomeFunction (SomeType.String is called before SomeType.SomeFunction).
  3319  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3320  						{Name: "SomeType.SomeFunction"},
  3321  					})
  3322  					expectSetFunctionBreakpointsResponse([]Breakpoint{{22, filepath.Base(fixture.Source), true, ""}})
  3323  
  3324  					// Expect the next breakpoint to be at SomeType.SomeFunction.
  3325  					client.ContinueRequest(1)
  3326  					client.ExpectContinueResponse(t)
  3327  
  3328  					if se := client.ExpectStoppedEvent(t); se.Body.Reason != "function breakpoint" || se.Body.ThreadId != 1 {
  3329  						t.Errorf("got %#v, want Reason=\"function breakpoint\", ThreadId=1", se)
  3330  					}
  3331  					checkStop(t, client, 1, "main.(*SomeType).SomeFunction", 22)
  3332  
  3333  					// Set a breakpoint at the next line in the program.
  3334  					client.SetBreakpointsRequest(fixture.Source, []int{23})
  3335  					got := client.ExpectSetBreakpointsResponse(t)
  3336  					if len(got.Body.Breakpoints) != 1 {
  3337  						t.Errorf("got %#v,\nwant len(Breakpoints)=%d", got, 1)
  3338  						return
  3339  					}
  3340  					bp := got.Body.Breakpoints[0]
  3341  					if bp.Line != 23 || bp.Verified != true || bp.Source.Path != fixture.Source {
  3342  						t.Errorf("got breakpoints[0] = %#v, \nwant Line=23 Verified=true Source.Path=%q", bp, fixture.Source)
  3343  					}
  3344  
  3345  					// Set a function breakpoint, this should not clear the breakpoint that was set in the previous setBreakpoints request.
  3346  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{
  3347  						{Name: "anotherFunction"},
  3348  					})
  3349  					expectSetFunctionBreakpointsResponse([]Breakpoint{{26, filepath.Base(fixture.Source), true, ""}})
  3350  
  3351  					// Expect the next breakpoint to be at line 23.
  3352  					client.ContinueRequest(1)
  3353  					client.ExpectContinueResponse(t)
  3354  
  3355  					if se := client.ExpectStoppedEvent(t); se.Body.Reason != "breakpoint" || se.Body.ThreadId != 1 {
  3356  						t.Errorf("got %#v, want Reason=\"breakpoint\", ThreadId=1", se)
  3357  					}
  3358  					checkStop(t, client, 1, "main.(*SomeType).SomeFunction", 23)
  3359  
  3360  					// Set a breakpoint, this should not clear the breakpoint that was set in the previous setFunctionBreakpoints request.
  3361  					client.SetBreakpointsRequest(fixture.Source, []int{37})
  3362  					got = client.ExpectSetBreakpointsResponse(t)
  3363  					if len(got.Body.Breakpoints) != 1 {
  3364  						t.Errorf("got %#v,\nwant len(Breakpoints)=%d", got, 1)
  3365  						return
  3366  					}
  3367  					bp = got.Body.Breakpoints[0]
  3368  					if bp.Line != 37 || bp.Verified != true || bp.Source.Path != fixture.Source {
  3369  						t.Errorf("got breakpoints[0] = %#v, \nwant Line=23 Verified=true Source.Path=%q", bp, fixture.Source)
  3370  					}
  3371  
  3372  					// Expect the next breakpoint to be at line anotherFunction.
  3373  					client.ContinueRequest(1)
  3374  					client.ExpectContinueResponse(t)
  3375  
  3376  					if se := client.ExpectStoppedEvent(t); se.Body.Reason != "function breakpoint" || se.Body.ThreadId != 1 {
  3377  						t.Errorf("got %#v, want Reason=\"function breakpoint\", ThreadId=1", se)
  3378  					}
  3379  					checkStop(t, client, 1, "main.anotherFunction", 26)
  3380  				},
  3381  				disconnect: true,
  3382  			}})
  3383  	})
  3384  }
  3385  
  3386  // TestLogPoints executes to a breakpoint and tests that log points
  3387  // send OutputEvents and do not halt program execution.
  3388  func TestLogPoints(t *testing.T) {
  3389  	runTest(t, "callme", func(client *daptest.Client, fixture protest.Fixture) {
  3390  		runDebugSessionWithBPs(t, client, "launch",
  3391  			// Launch
  3392  			func() {
  3393  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  3394  			},
  3395  			// Set breakpoints
  3396  			fixture.Source, []int{23},
  3397  			[]onBreakpoint{{
  3398  				// Stop at line 23
  3399  				execute: func() {
  3400  					checkStop(t, client, 1, "main.main", 23)
  3401  					bps := []int{6, 25, 27, 16}
  3402  					logMessages := map[int]string{6: "{i*2}: in callme!", 16: "in callme2!"}
  3403  					client.SetBreakpointsRequestWithArgs(fixture.Source, bps, nil, nil, logMessages)
  3404  					client.ExpectSetBreakpointsResponse(t)
  3405  
  3406  					client.ContinueRequest(1)
  3407  					client.ExpectContinueResponse(t)
  3408  
  3409  					for i := 0; i < 5; i++ {
  3410  						se := client.ExpectStoppedEvent(t)
  3411  						if se.Body.Reason != "breakpoint" || se.Body.ThreadId != 1 {
  3412  							t.Errorf("got stopped event = %#v, \nwant Reason=\"breakpoint\" ThreadId=1", se)
  3413  						}
  3414  						checkStop(t, client, 1, "main.main", 25)
  3415  
  3416  						client.ContinueRequest(1)
  3417  						client.ExpectContinueResponse(t)
  3418  						checkLogMessage(t, client.ExpectOutputEvent(t), 1, fmt.Sprintf("%d: in callme!", i*2), fixture.Source, 6)
  3419  					}
  3420  					se := client.ExpectStoppedEvent(t)
  3421  					if se.Body.Reason != "breakpoint" || se.Body.ThreadId != 1 {
  3422  						t.Errorf("got stopped event = %#v, \nwant Reason=\"breakpoint\" ThreadId=1", se)
  3423  					}
  3424  					checkStop(t, client, 1, "main.main", 27)
  3425  
  3426  					client.NextRequest(1)
  3427  					client.ExpectNextResponse(t)
  3428  
  3429  					checkLogMessage(t, client.ExpectOutputEvent(t), 1, "in callme2!", fixture.Source, 16)
  3430  
  3431  					se = client.ExpectStoppedEvent(t)
  3432  					if se.Body.Reason != "step" || se.Body.ThreadId != 1 {
  3433  						t.Errorf("got stopped event = %#v, \nwant Reason=\"step\" ThreadId=1", se)
  3434  					}
  3435  					checkStop(t, client, 1, "main.main", 28)
  3436  				},
  3437  				disconnect: true,
  3438  			}})
  3439  	})
  3440  }
  3441  
  3442  // TestLogPointsShowFullValue tests that log points will not truncate the string value.
  3443  func TestLogPointsShowFullValue(t *testing.T) {
  3444  	runTest(t, "longstrings", func(client *daptest.Client, fixture protest.Fixture) {
  3445  		runDebugSessionWithBPs(t, client, "launch",
  3446  			// Launch
  3447  			func() {
  3448  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  3449  			},
  3450  			// Set breakpoints
  3451  			fixture.Source, []int{16},
  3452  			[]onBreakpoint{{
  3453  				execute: func() {
  3454  					checkStop(t, client, 1, "main.main", 16)
  3455  					bps := []int{19}
  3456  					logMessages := map[int]string{19: "{&s4097}"}
  3457  					client.SetBreakpointsRequestWithArgs(fixture.Source, bps, nil, nil, logMessages)
  3458  					client.ExpectSetBreakpointsResponse(t)
  3459  
  3460  					client.ContinueRequest(1)
  3461  					client.ExpectContinueResponse(t)
  3462  					checkLogMessage(t, client.ExpectOutputEvent(t), 1, "*\"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...+3585 more\"", fixture.Source, 19)
  3463  
  3464  					se := client.ExpectStoppedEvent(t)
  3465  					if se.Body.Reason != "breakpoint" || se.Body.ThreadId != 1 {
  3466  						t.Errorf("got stopped event = %#v, \nwant Reason=\"breakpoint\" ThreadId=1", se)
  3467  					}
  3468  					checkStop(t, client, 1, "main.main", 22)
  3469  				},
  3470  				disconnect: true,
  3471  			}})
  3472  	})
  3473  }
  3474  
  3475  func checkLogMessage(t *testing.T, oe *dap.OutputEvent, goid int, text, path string, line int) {
  3476  	t.Helper()
  3477  	prefix := "> [Go "
  3478  	if goid >= 0 {
  3479  		prefix += strconv.Itoa(goid) + "]"
  3480  	}
  3481  	if oe.Body.Category != "stdout" || !strings.HasPrefix(oe.Body.Output, prefix) || !strings.HasSuffix(oe.Body.Output, text+"\n") {
  3482  		t.Errorf("got output event = %#v, \nwant Category=\"stdout\" Output=\"%s: %s\\n\"", oe, prefix, text)
  3483  	}
  3484  	if oe.Body.Line != line || oe.Body.Source == nil || oe.Body.Source.Path != path {
  3485  		t.Errorf("got output event = %#v, \nwant Line=%d Source.Path=%s", oe, line, path)
  3486  	}
  3487  }
  3488  
  3489  // TestHaltPreventsAutoResume tests that a pause request issued while processing
  3490  // log messages will result in a real stop.
  3491  func TestHaltPreventsAutoResume(t *testing.T) {
  3492  	runTest(t, "callme", func(client *daptest.Client, fixture protest.Fixture) {
  3493  		runDebugSessionWithBPs(t, client, "launch", // Launch
  3494  			func() {
  3495  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  3496  			},
  3497  			// Set breakpoints
  3498  			fixture.Source, []int{23},
  3499  			[]onBreakpoint{{
  3500  				execute: func() {
  3501  					savedResumeOnce := resumeOnceAndCheckStop
  3502  					defer func() {
  3503  						resumeOnceAndCheckStop = savedResumeOnce
  3504  					}()
  3505  					checkStop(t, client, 1, "main.main", 23)
  3506  					bps := []int{6, 25}
  3507  					logMessages := map[int]string{6: "in callme!"}
  3508  					client.SetBreakpointsRequestWithArgs(fixture.Source, bps, nil, nil, logMessages)
  3509  					client.ExpectSetBreakpointsResponse(t)
  3510  
  3511  					for i := 0; i < 5; i++ {
  3512  						// Reset the handler to the default behavior.
  3513  						resumeOnceAndCheckStop = savedResumeOnce
  3514  
  3515  						// Expect a pause request while stopped not to interrupt continue.
  3516  						client.PauseRequest(1)
  3517  						client.ExpectPauseResponse(t)
  3518  
  3519  						client.ContinueRequest(1)
  3520  						client.ExpectContinueResponse(t)
  3521  						se := client.ExpectStoppedEvent(t)
  3522  						if se.Body.Reason != "breakpoint" || se.Body.ThreadId != 1 {
  3523  							t.Errorf("got stopped event = %#v, \nwant Reason=\"breakpoint\" ThreadId=1", se)
  3524  						}
  3525  						checkStop(t, client, 1, "main.main", 25)
  3526  
  3527  						pauseDoneChan := make(chan struct{}, 1)
  3528  						outputDoneChan := make(chan struct{}, 1)
  3529  						// Send a halt request when trying to resume the program after being
  3530  						// interrupted. This should allow the log message to be processed,
  3531  						// but keep the process from continuing beyond the line.
  3532  						resumeOnceAndCheckStop = func(s *Session, command string, allowNextStateChange *syncflag) (*api.DebuggerState, error) {
  3533  							// This should trigger after the log message is sent, but before
  3534  							// execution is resumed.
  3535  							if command == api.DirectionCongruentContinue {
  3536  								go func() {
  3537  									<-outputDoneChan
  3538  									defer close(pauseDoneChan)
  3539  									client.PauseRequest(1)
  3540  									client.ExpectPauseResponse(t)
  3541  								}()
  3542  								// Wait for the pause to be complete.
  3543  								<-pauseDoneChan
  3544  							}
  3545  							return s.resumeOnceAndCheckStop(command, allowNextStateChange)
  3546  						}
  3547  
  3548  						client.ContinueRequest(1)
  3549  						client.ExpectContinueResponse(t)
  3550  						checkLogMessage(t, client.ExpectOutputEvent(t), 1, "in callme!", fixture.Source, 6)
  3551  						// Signal that the output event has been received.
  3552  						close(outputDoneChan)
  3553  						// Wait for the pause to be complete.
  3554  						<-pauseDoneChan
  3555  						se = client.ExpectStoppedEvent(t)
  3556  						if se.Body.Reason != "pause" {
  3557  							t.Errorf("got stopped event = %#v, \nwant Reason=\"pause\"", se)
  3558  						}
  3559  						checkStop(t, client, 1, "main.callme", 6)
  3560  					}
  3561  				},
  3562  				disconnect: true,
  3563  			}})
  3564  	})
  3565  }
  3566  
  3567  // TestConcurrentBreakpointsLogPoints tests that a breakpoint set in the main
  3568  // goroutine is hit the correct number of times and log points set in the
  3569  // children goroutines produce the correct number of output events.
  3570  func TestConcurrentBreakpointsLogPoints(t *testing.T) {
  3571  	if runtime.GOOS == "windows" && runtime.GOARCH == "arm64" {
  3572  		t.Skip("broken")
  3573  	}
  3574  	tests := []struct {
  3575  		name        string
  3576  		fixture     string
  3577  		start       int
  3578  		breakpoints []int
  3579  	}{
  3580  		{
  3581  			name:        "source breakpoints",
  3582  			fixture:     "goroutinestackprog",
  3583  			breakpoints: []int{23},
  3584  		},
  3585  		{
  3586  			name:    "hardcoded breakpoint",
  3587  			fixture: "goroutinebreak",
  3588  		},
  3589  	}
  3590  	for _, tt := range tests {
  3591  		t.Run(tt.name, func(t *testing.T) {
  3592  			runTest(t, tt.fixture, func(client *daptest.Client, fixture protest.Fixture) {
  3593  				client.InitializeRequest()
  3594  				client.ExpectInitializeResponseAndCapabilities(t)
  3595  
  3596  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  3597  				client.ExpectInitializedEvent(t)
  3598  				client.ExpectLaunchResponse(t)
  3599  
  3600  				bps := append([]int{8}, tt.breakpoints...)
  3601  				logMessages := map[int]string{8: "hello"}
  3602  				client.SetBreakpointsRequestWithArgs(fixture.Source, bps, nil, nil, logMessages)
  3603  				client.ExpectSetBreakpointsResponse(t)
  3604  
  3605  				client.ConfigurationDoneRequest()
  3606  				client.ExpectConfigurationDoneResponse(t)
  3607  
  3608  				// There may be up to 1 breakpoint and any number of log points that are
  3609  				// hit concurrently. We should get a stopped event every time the breakpoint
  3610  				// is hit and an output event for each log point hit.
  3611  				var oeCount, seCount int
  3612  				for oeCount < 10 || seCount < 10 {
  3613  					switch m := client.ExpectMessage(t).(type) {
  3614  					case *dap.StoppedEvent:
  3615  						if m.Body.Reason != "breakpoint" || !m.Body.AllThreadsStopped || m.Body.ThreadId != 1 {
  3616  							t.Errorf("\ngot  %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1", m)
  3617  						}
  3618  						seCount++
  3619  						client.ContinueRequest(1)
  3620  					case *dap.OutputEvent:
  3621  						checkLogMessage(t, m, -1, "hello", fixture.Source, 8)
  3622  						oeCount++
  3623  					case *dap.ContinueResponse:
  3624  					case *dap.TerminatedEvent:
  3625  						t.Fatalf("\nexpected 10 output events and 10 stopped events, got %d output events and %d stopped events", oeCount, seCount)
  3626  					default:
  3627  						t.Fatalf("Unexpected message type: expect StoppedEvent, OutputEvent, or ContinueResponse, got %#v", m)
  3628  					}
  3629  				}
  3630  				// TODO(suzmue): The dap server may identify some false
  3631  				// positives for hard coded breakpoints, so there may still
  3632  				// be more stopped events.
  3633  				client.DisconnectRequestWithKillOption(true)
  3634  			})
  3635  		})
  3636  	}
  3637  }
  3638  
  3639  func TestSetBreakpointWhileRunning(t *testing.T) {
  3640  	runTest(t, "integrationprog", func(client *daptest.Client, fixture protest.Fixture) {
  3641  		runDebugSessionWithBPs(t, client, "launch",
  3642  			// Launch
  3643  			func() {
  3644  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  3645  			},
  3646  			// Set breakpoints
  3647  			fixture.Source, []int{16},
  3648  			[]onBreakpoint{{
  3649  				execute: func() {
  3650  					// The program loops 3 times over lines 14-15-8-9-10-16
  3651  					checkStop(t, client, 1, "main.main", 16) // Line that sleeps for 1 second
  3652  
  3653  					// We can set breakpoints while nexting
  3654  					client.NextRequest(1)
  3655  					client.ExpectNextResponse(t)
  3656  					client.SetBreakpointsRequest(fixture.Source, []int{15}) // [16,] => [15,]
  3657  					checkSetBreakpointsResponse(t, []Breakpoint{{15, fixture.Source, true, ""}}, client.ExpectSetBreakpointsResponse(t))
  3658  					se := client.ExpectStoppedEvent(t)
  3659  					if se.Body.Reason != "step" || !se.Body.AllThreadsStopped || se.Body.ThreadId != 1 {
  3660  						t.Errorf("\ngot  %#v\nwant Reason='step' AllThreadsStopped=true ThreadId=1", se)
  3661  					}
  3662  					checkStop(t, client, 1, "main.main", 14)
  3663  					client.ContinueRequest(1)
  3664  					client.ExpectContinueResponse(t)
  3665  					se = client.ExpectStoppedEvent(t)
  3666  					if se.Body.Reason != "breakpoint" || !se.Body.AllThreadsStopped || se.Body.ThreadId != 1 {
  3667  						t.Errorf("\ngot  %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1", se)
  3668  					}
  3669  					checkStop(t, client, 1, "main.main", 15)
  3670  
  3671  					// We can set breakpoints while continuing
  3672  					client.ContinueRequest(1)
  3673  					client.ExpectContinueResponse(t)
  3674  					client.SetBreakpointsRequest(fixture.Source, []int{9}) // [15,] => [9,]
  3675  					checkSetBreakpointsResponse(t, []Breakpoint{{9, fixture.Source, true, ""}}, client.ExpectSetBreakpointsResponse(t))
  3676  					se = client.ExpectStoppedEvent(t)
  3677  					if se.Body.Reason != "breakpoint" || !se.Body.AllThreadsStopped || se.Body.ThreadId != 1 {
  3678  						t.Errorf("\ngot  %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1", se)
  3679  					}
  3680  					checkStop(t, client, 1, "main.sayhi", 9)
  3681  				},
  3682  				disconnect: true,
  3683  			}})
  3684  	})
  3685  }
  3686  
  3687  func TestSetFunctionBreakpointWhileRunning(t *testing.T) {
  3688  	runTest(t, "integrationprog", func(client *daptest.Client, fixture protest.Fixture) {
  3689  		runDebugSessionWithBPs(t, client, "launch",
  3690  			// Launch
  3691  			func() {
  3692  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  3693  			},
  3694  			// Set breakpoints
  3695  			fixture.Source, []int{16},
  3696  			[]onBreakpoint{{
  3697  				execute: func() {
  3698  					// The program loops 3 times over lines 14-15-8-9-10-16
  3699  					checkStop(t, client, 1, "main.main", 16) // Line that sleeps for 1 second
  3700  
  3701  					// We can set breakpoints while nexting
  3702  					client.NextRequest(1)
  3703  					client.ExpectNextResponse(t)
  3704  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{{Name: "main.sayhi"}}) // [16,] => [16, 8]
  3705  					checkBreakpoints(t, []Breakpoint{{8, fixture.Source, true, ""}}, client.ExpectSetFunctionBreakpointsResponse(t).Body.Breakpoints)
  3706  					client.SetBreakpointsRequest(fixture.Source, []int{}) // [16,8] => [8]
  3707  					expectSetBreakpointsResponse(t, client, []Breakpoint{})
  3708  					se := client.ExpectStoppedEvent(t)
  3709  					if se.Body.Reason != "step" || !se.Body.AllThreadsStopped || se.Body.ThreadId != 1 {
  3710  						t.Errorf("\ngot  %#v\nwant Reason='step' AllThreadsStopped=true ThreadId=1", se)
  3711  					}
  3712  					checkStop(t, client, 1, "main.main", 14)
  3713  
  3714  					// Make sure we can hit the breakpoints.
  3715  					client.ContinueRequest(1)
  3716  					client.ExpectContinueResponse(t)
  3717  					se = client.ExpectStoppedEvent(t)
  3718  					if se.Body.Reason != "function breakpoint" || !se.Body.AllThreadsStopped || se.Body.ThreadId != 1 {
  3719  						t.Errorf("\ngot  %#v\nwant Reason='function breakpoint' AllThreadsStopped=true ThreadId=1", se)
  3720  					}
  3721  					checkStop(t, client, 1, "main.sayhi", 8)
  3722  
  3723  					// We can set breakpoints while continuing
  3724  					client.ContinueRequest(1)
  3725  					client.ExpectContinueResponse(t)
  3726  					client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{}) // [8,] => []
  3727  					checkBreakpoints(t, []Breakpoint{}, client.ExpectSetFunctionBreakpointsResponse(t).Body.Breakpoints)
  3728  					client.SetBreakpointsRequest(fixture.Source, []int{16}) // [] => [16]
  3729  					expectSetBreakpointsResponse(t, client, []Breakpoint{{16, fixture.Source, true, ""}})
  3730  					se = client.ExpectStoppedEvent(t)
  3731  					if se.Body.Reason != "breakpoint" || !se.Body.AllThreadsStopped || se.Body.ThreadId != 1 {
  3732  						t.Errorf("\ngot  %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1", se)
  3733  					}
  3734  					checkStop(t, client, 1, "main.main", 16)
  3735  				},
  3736  				disconnect: true,
  3737  			}})
  3738  	})
  3739  }
  3740  
  3741  func TestHitConditionBreakpoints(t *testing.T) {
  3742  	runTest(t, "break", func(client *daptest.Client, fixture protest.Fixture) {
  3743  		runDebugSessionWithBPs(t, client, "launch",
  3744  			// Launch
  3745  			func() {
  3746  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  3747  			},
  3748  			// Set breakpoints
  3749  			fixture.Source, []int{4},
  3750  			[]onBreakpoint{{
  3751  				execute: func() {
  3752  					client.SetBreakpointsRequestWithArgs(fixture.Source, []int{7}, nil, map[int]string{7: "4"}, nil)
  3753  					expectSetBreakpointsResponse(t, client, []Breakpoint{{7, fixture.Source, true, ""}})
  3754  
  3755  					client.ContinueRequest(1)
  3756  					client.ExpectContinueResponse(t)
  3757  					client.ExpectStoppedEvent(t)
  3758  					checkStop(t, client, 1, "main.main", 7)
  3759  
  3760  					// Check that we are stopped at the correct value of i.
  3761  					client.VariablesRequest(localsScope)
  3762  					locals := client.ExpectVariablesResponse(t)
  3763  					checkVarExact(t, locals, 0, "i", "i", "4", "int", noChildren)
  3764  
  3765  					// Change the hit condition.
  3766  					client.SetBreakpointsRequestWithArgs(fixture.Source, []int{7}, nil, map[int]string{7: "% 2"}, nil)
  3767  					expectSetBreakpointsResponse(t, client, []Breakpoint{{7, fixture.Source, true, ""}})
  3768  
  3769  					client.ContinueRequest(1)
  3770  					client.ExpectContinueResponse(t)
  3771  					client.ExpectStoppedEvent(t)
  3772  					checkStop(t, client, 1, "main.main", 7)
  3773  
  3774  					// Check that we are stopped at the correct value of i.
  3775  					client.VariablesRequest(localsScope)
  3776  					locals = client.ExpectVariablesResponse(t)
  3777  					checkVarExact(t, locals, 0, "i", "i", "6", "int", noChildren)
  3778  
  3779  					// Expect an error if an assignment is passed.
  3780  					client.SetBreakpointsRequestWithArgs(fixture.Source, []int{7}, nil, map[int]string{7: "= 2"}, nil)
  3781  					expectSetBreakpointsResponse(t, client, []Breakpoint{{-1, "", false, ""}})
  3782  
  3783  					// Change the hit condition.
  3784  					client.SetBreakpointsRequestWithArgs(fixture.Source, []int{7}, nil, map[int]string{7: "< 8"}, nil)
  3785  					expectSetBreakpointsResponse(t, client, []Breakpoint{{7, fixture.Source, true, ""}})
  3786  					client.ContinueRequest(1)
  3787  					client.ExpectContinueResponse(t)
  3788  					client.ExpectStoppedEvent(t)
  3789  					checkStop(t, client, 1, "main.main", 7)
  3790  
  3791  					// Check that we are stopped at the correct value of i.
  3792  					client.VariablesRequest(localsScope)
  3793  					locals = client.ExpectVariablesResponse(t)
  3794  					checkVarExact(t, locals, 0, "i", "i", "7", "int", noChildren)
  3795  
  3796  					client.ContinueRequest(1)
  3797  					client.ExpectContinueResponse(t)
  3798  
  3799  					client.ExpectTerminatedEvent(t)
  3800  				},
  3801  				disconnect: false,
  3802  			}})
  3803  	})
  3804  }
  3805  
  3806  // TestLaunchSubstitutePath sets a breakpoint using a path
  3807  // that does not exist and expects the substitutePath attribute
  3808  // in the launch configuration to take care of the mapping.
  3809  func TestLaunchSubstitutePath(t *testing.T) {
  3810  	runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
  3811  		substitutePathTestHelper(t, fixture, client, "launch", map[string]interface{}{"mode": "exec", "program": fixture.Path})
  3812  	})
  3813  }
  3814  
  3815  // TestAttachSubstitutePath sets a breakpoint using a path
  3816  // that does not exist and expects the substitutePath attribute
  3817  // in the launch configuration to take care of the mapping.
  3818  func TestAttachSubstitutePath(t *testing.T) {
  3819  	if runtime.GOOS == "windows" {
  3820  		t.Skip("test skipped on Windows, see https://delve.teamcity.com/project/Delve_windows for details")
  3821  	}
  3822  	runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
  3823  		cmd := execFixture(t, fixture)
  3824  
  3825  		substitutePathTestHelper(t, fixture, client, "attach", map[string]interface{}{"mode": "local", "processId": cmd.Process.Pid})
  3826  	})
  3827  }
  3828  
  3829  func substitutePathTestHelper(t *testing.T, fixture protest.Fixture, client *daptest.Client, request string, launchAttachConfig map[string]interface{}) {
  3830  	t.Helper()
  3831  	nonexistentDir := filepath.Join(string(filepath.Separator), "path", "that", "does", "not", "exist")
  3832  	if runtime.GOOS == "windows" {
  3833  		nonexistentDir = "C:" + nonexistentDir
  3834  	}
  3835  
  3836  	launchAttachConfig["stopOnEntry"] = false
  3837  	// The rules in 'substitutePath' will be applied as follows:
  3838  	// - mapping paths from client to server:
  3839  	//		The first rule["from"] to match a prefix of 'path' will be applied:
  3840  	//			strings.Replace(path, rule["from"], rule["to"], 1)
  3841  	// - mapping paths from server to client:
  3842  	//		The first rule["to"] to match a prefix of 'path' will be applied:
  3843  	//			strings.Replace(path, rule["to"], rule["from"], 1)
  3844  	launchAttachConfig["substitutePath"] = []map[string]string{
  3845  		{"from": nonexistentDir, "to": filepath.Dir(fixture.Source)},
  3846  		// Since the path mappings are ordered, when converting from client path to
  3847  		// server path, this mapping will not apply, because nonexistentDir appears in
  3848  		// an earlier rule.
  3849  		{"from": nonexistentDir, "to": "this_is_a_bad_path"},
  3850  		// Since the path mappings are ordered, when converting from server path to
  3851  		// client path, this mapping will not apply, because filepath.Dir(fixture.Source)
  3852  		// appears in an earlier rule.
  3853  		{"from": "this_is_a_bad_path", "to": filepath.Dir(fixture.Source)},
  3854  	}
  3855  
  3856  	runDebugSessionWithBPs(t, client, request,
  3857  		func() {
  3858  			switch request {
  3859  			case "attach":
  3860  				client.AttachRequest(launchAttachConfig)
  3861  				client.ExpectCapabilitiesEventSupportTerminateDebuggee(t)
  3862  			case "launch":
  3863  				client.LaunchRequestWithArgs(launchAttachConfig)
  3864  			default:
  3865  				t.Fatalf("invalid request: %s", request)
  3866  			}
  3867  		},
  3868  		// Set breakpoints
  3869  		filepath.Join(nonexistentDir, "loopprog.go"), []int{8},
  3870  		[]onBreakpoint{{
  3871  			execute: func() {
  3872  				checkStop(t, client, 1, "main.loop", 8)
  3873  			},
  3874  			disconnect: true,
  3875  		}})
  3876  }
  3877  
  3878  // execFixture runs the binary fixture.Path and hooks up stdout and stderr
  3879  // to os.Stdout and os.Stderr.
  3880  func execFixture(t *testing.T, fixture protest.Fixture) *exec.Cmd {
  3881  	t.Helper()
  3882  	// TODO(polina): do I need to sanity check testBackend and runtime.GOOS?
  3883  	cmd := exec.Command(fixture.Path)
  3884  	cmd.Stdout = os.Stdout
  3885  	cmd.Stderr = os.Stderr
  3886  	if err := cmd.Start(); err != nil {
  3887  		t.Fatal(err)
  3888  	}
  3889  	return cmd
  3890  }
  3891  
  3892  // TestWorkingDir executes to a breakpoint and tests that the specified
  3893  // working directory is the one used to run the program.
  3894  func TestWorkingDir(t *testing.T) {
  3895  	runTest(t, "workdir", func(client *daptest.Client, fixture protest.Fixture) {
  3896  		wd := os.TempDir()
  3897  		// For Darwin `os.TempDir()` returns `/tmp` which is symlink to `/private/tmp`.
  3898  		if runtime.GOOS == "darwin" {
  3899  			wd = "/private/tmp"
  3900  		}
  3901  		runDebugSessionWithBPs(t, client, "launch",
  3902  			// Launch
  3903  			func() {
  3904  				client.LaunchRequestWithArgs(map[string]interface{}{
  3905  					"mode":        "exec",
  3906  					"program":     fixture.Path,
  3907  					"stopOnEntry": false,
  3908  					"cwd":         wd,
  3909  				})
  3910  			},
  3911  			// Set breakpoints
  3912  			fixture.Source, []int{10}, // b main.main
  3913  			[]onBreakpoint{{
  3914  				execute: func() {
  3915  					checkStop(t, client, 1, "main.main", 10)
  3916  					client.VariablesRequest(localsScope)
  3917  					locals := client.ExpectVariablesResponse(t)
  3918  					checkChildren(t, locals, "Locals", 2)
  3919  					for i := range locals.Body.Variables {
  3920  						switch locals.Body.Variables[i].Name {
  3921  						case "pwd":
  3922  							checkVarExact(t, locals, i, "pwd", "pwd", fmt.Sprintf("%q", wd), "string", noChildren)
  3923  						case "err":
  3924  							checkVarExact(t, locals, i, "err", "err", "error nil", "error", noChildren)
  3925  						}
  3926  					}
  3927  				},
  3928  				disconnect: false,
  3929  			}})
  3930  	})
  3931  }
  3932  
  3933  // checkEval is a helper for verifying the values within an EvaluateResponse.
  3934  //
  3935  //	value - the value of the evaluated expression
  3936  //	hasRef - true if the evaluated expression should have children and therefore a non-0 variable reference
  3937  //	ref - reference to retrieve children of this evaluated expression (0 if none)
  3938  func checkEval(t *testing.T, got *dap.EvaluateResponse, value string, hasRef bool) (ref int) {
  3939  	t.Helper()
  3940  	if got.Body.Result != value || (got.Body.VariablesReference > 0) != hasRef {
  3941  		t.Errorf("\ngot  %#v\nwant Result=%q hasRef=%t", got, value, hasRef)
  3942  	}
  3943  	return got.Body.VariablesReference
  3944  }
  3945  
  3946  // checkEvalIndexed is a helper for verifying the values within an EvaluateResponse.
  3947  //
  3948  //	value - the value of the evaluated expression
  3949  //	hasRef - true if the evaluated expression should have children and therefore a non-0 variable reference
  3950  //	ref - reference to retrieve children of this evaluated expression (0 if none)
  3951  func checkEvalIndexed(t *testing.T, got *dap.EvaluateResponse, value string, hasRef bool, indexed, named int) (ref int) {
  3952  	t.Helper()
  3953  	if got.Body.Result != value || (got.Body.VariablesReference > 0) != hasRef || got.Body.IndexedVariables != indexed || got.Body.NamedVariables != named {
  3954  		t.Errorf("\ngot  %#v\nwant Result=%q hasRef=%t IndexedVariables=%d NamedVariables=%d", got, value, hasRef, indexed, named)
  3955  	}
  3956  	return got.Body.VariablesReference
  3957  }
  3958  
  3959  func checkEvalRegex(t *testing.T, got *dap.EvaluateResponse, valueRegex string, hasRef bool) (ref int) {
  3960  	t.Helper()
  3961  	matched, _ := regexp.MatchString(valueRegex, got.Body.Result)
  3962  	if !matched || (got.Body.VariablesReference > 0) != hasRef {
  3963  		t.Errorf("\ngot  %#v\nwant Result=%q hasRef=%t", got, valueRegex, hasRef)
  3964  	}
  3965  	return got.Body.VariablesReference
  3966  }
  3967  
  3968  func TestEvaluateRequest(t *testing.T) {
  3969  	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {
  3970  		runDebugSessionWithBPs(t, client, "launch",
  3971  			// Launch
  3972  			func() {
  3973  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  3974  			},
  3975  			fixture.Source, []int{}, // Breakpoint set in the program
  3976  			[]onBreakpoint{{ // Stop at first breakpoint
  3977  				execute: func() {
  3978  					checkStop(t, client, 1, "main.foobar", []int{65, 66})
  3979  
  3980  					// Variable lookup
  3981  					client.EvaluateRequest("a2", 1000, "this context will be ignored")
  3982  					got := client.ExpectEvaluateResponse(t)
  3983  					checkEval(t, got, "6", noChildren)
  3984  
  3985  					client.EvaluateRequest("a5", 1000, "this context will be ignored")
  3986  					got = client.ExpectEvaluateResponse(t)
  3987  					ref := checkEval(t, got, "[]int len: 5, cap: 5, [1,2,3,4,5]", hasChildren)
  3988  					if ref > 0 {
  3989  						client.VariablesRequest(ref)
  3990  						a5 := client.ExpectVariablesResponse(t)
  3991  						checkChildren(t, a5, "a5", 5)
  3992  						checkVarExact(t, a5, 0, "[0]", "(a5)[0]", "1", "int", noChildren)
  3993  						checkVarExact(t, a5, 4, "[4]", "(a5)[4]", "5", "int", noChildren)
  3994  						validateEvaluateName(t, client, a5, 0)
  3995  						validateEvaluateName(t, client, a5, 4)
  3996  					}
  3997  
  3998  					// Variable lookup that's not fully loaded
  3999  					client.EvaluateRequest("ba", 1000, "this context will be ignored")
  4000  					got = client.ExpectEvaluateResponse(t)
  4001  					checkEvalIndexed(t, got, "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]", hasChildren, 200, 0)
  4002  
  4003  					// All (binary and unary) on basic types except <-, ++ and --
  4004  					client.EvaluateRequest("1+1", 1000, "this context will be ignored")
  4005  					got = client.ExpectEvaluateResponse(t)
  4006  					checkEval(t, got, "2", noChildren)
  4007  
  4008  					// Comparison operators on any type
  4009  					client.EvaluateRequest("1<2", 1000, "this context will be ignored")
  4010  					got = client.ExpectEvaluateResponse(t)
  4011  					checkEval(t, got, "true", noChildren)
  4012  
  4013  					// Type casts between numeric types
  4014  					client.EvaluateRequest("int(2.3)", 1000, "this context will be ignored")
  4015  					got = client.ExpectEvaluateResponse(t)
  4016  					checkEval(t, got, "2", noChildren)
  4017  
  4018  					// Type casts of integer constants into any pointer type and vice versa
  4019  					client.EvaluateRequest("(*int)(2)", 1000, "this context will be ignored")
  4020  					got = client.ExpectEvaluateResponse(t)
  4021  					ref = checkEvalRegex(t, got, `\*\(unreadable .+\)`, hasChildren)
  4022  					if ref > 0 {
  4023  						client.VariablesRequest(ref)
  4024  						val := client.ExpectVariablesResponse(t)
  4025  						checkChildren(t, val, "*(*int)(2)", 1)
  4026  						checkVarRegex(t, val, 0, "^$", `\(\*\(\(\*int\)\(2\)\)\)`, `\(unreadable .+\)`, "int", noChildren)
  4027  						validateEvaluateName(t, client, val, 0)
  4028  					}
  4029  
  4030  					// Type casts between string, []byte and []rune
  4031  					client.EvaluateRequest("[]byte(\"ABC€\")", 1000, "this context will be ignored")
  4032  					got = client.ExpectEvaluateResponse(t)
  4033  					checkEvalIndexed(t, got, "[]uint8 len: 6, cap: 6, [65,66,67,226,130,172]", noChildren, 6, 1)
  4034  
  4035  					// Struct member access (i.e. somevar.memberfield)
  4036  					client.EvaluateRequest("ms.Nest.Level", 1000, "this context will be ignored")
  4037  					got = client.ExpectEvaluateResponse(t)
  4038  					checkEval(t, got, "1", noChildren)
  4039  
  4040  					// Slicing and indexing operators on arrays, slices and strings
  4041  					client.EvaluateRequest("a5[4]", 1000, "this context will be ignored")
  4042  					got = client.ExpectEvaluateResponse(t)
  4043  					checkEval(t, got, "5", noChildren)
  4044  
  4045  					// Map access
  4046  					client.EvaluateRequest("mp[1]", 1000, "this context will be ignored")
  4047  					got = client.ExpectEvaluateResponse(t)
  4048  					ref = checkEval(t, got, "interface {}(int) 42", hasChildren)
  4049  					if ref > 0 {
  4050  						client.VariablesRequest(ref)
  4051  						expr := client.ExpectVariablesResponse(t)
  4052  						checkChildren(t, expr, "mp[1]", 1)
  4053  						checkVarExact(t, expr, 0, "data", "(mp[1]).(data)", "42", "int", noChildren)
  4054  						validateEvaluateName(t, client, expr, 0)
  4055  					}
  4056  
  4057  					// Pointer dereference
  4058  					client.EvaluateRequest("*ms.Nest", 1000, "this context will be ignored")
  4059  					got = client.ExpectEvaluateResponse(t)
  4060  					ref = checkEvalRegex(t, got, `main\.Nest {Level: 1, Nest: \*main.Nest {Level: 2, Nest: \*\(\*main\.Nest\)\(0x[0-9a-f]+\)}}`, hasChildren)
  4061  					if ref > 0 {
  4062  						client.VariablesRequest(ref)
  4063  						expr := client.ExpectVariablesResponse(t)
  4064  						checkChildren(t, expr, "*ms.Nest", 2)
  4065  						checkVarExact(t, expr, 0, "Level", "(*ms.Nest).Level", "1", "int", noChildren)
  4066  						validateEvaluateName(t, client, expr, 0)
  4067  					}
  4068  
  4069  					// Calls to builtin functions: cap, len, complex, imag and real
  4070  					client.EvaluateRequest("len(a5)", 1000, "this context will be ignored")
  4071  					got = client.ExpectEvaluateResponse(t)
  4072  					checkEval(t, got, "5", noChildren)
  4073  
  4074  					// Type assertion on interface variables (i.e. somevar.(concretetype))
  4075  					client.EvaluateRequest("mp[1].(int)", 1000, "this context will be ignored")
  4076  					got = client.ExpectEvaluateResponse(t)
  4077  					checkEval(t, got, "42", noChildren)
  4078  				},
  4079  				disconnect: false,
  4080  			}, { // Stop at second breakpoint
  4081  				execute: func() {
  4082  					checkStop(t, client, 1, "main.barfoo", 27)
  4083  
  4084  					// Top-most frame
  4085  					client.EvaluateRequest("a1", 1000, "this context will be ignored")
  4086  					got := client.ExpectEvaluateResponse(t)
  4087  					checkEval(t, got, "\"bur\"", noChildren)
  4088  					// No frame defaults to top-most frame
  4089  					client.EvaluateRequest("a1", 0, "this context will be ignored")
  4090  					got = client.ExpectEvaluateResponse(t)
  4091  					checkEval(t, got, "\"bur\"", noChildren)
  4092  					// Next frame
  4093  					client.EvaluateRequest("a1", 1001, "this context will be ignored")
  4094  					got = client.ExpectEvaluateResponse(t)
  4095  					checkEval(t, got, "\"foofoofoofoofoofoo\"", noChildren)
  4096  					// Next frame
  4097  					client.EvaluateRequest("a1", 1002, "any context but watch")
  4098  					erres := client.ExpectVisibleErrorResponse(t)
  4099  					if !checkErrorMessageFormat(erres.Body.Error, "Unable to evaluate expression: could not find symbol value for a1") {
  4100  						t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"", erres)
  4101  					}
  4102  					client.EvaluateRequest("a1", 1002, "watch")
  4103  					erres = client.ExpectInvisibleErrorResponse(t)
  4104  					if !checkErrorMessageFormat(erres.Body.Error, "Unable to evaluate expression: could not find symbol value for a1") {
  4105  						t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"", erres)
  4106  					}
  4107  					client.EvaluateRequest("a1", 1002, "repl")
  4108  					erres = client.ExpectInvisibleErrorResponse(t)
  4109  					if !checkErrorMessageFormat(erres.Body.Error, "Unable to evaluate expression: could not find symbol value for a1") {
  4110  						t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"", erres)
  4111  					}
  4112  					client.EvaluateRequest("a1", 1002, "hover")
  4113  					erres = client.ExpectInvisibleErrorResponse(t)
  4114  					if !checkErrorMessageFormat(erres.Body.Error, "Unable to evaluate expression: could not find symbol value for a1") {
  4115  						t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"", erres)
  4116  					}
  4117  					client.EvaluateRequest("a1", 1002, "clipboard")
  4118  					erres = client.ExpectVisibleErrorResponse(t)
  4119  					if !checkErrorMessageFormat(erres.Body.Error, "Unable to evaluate expression: could not find symbol value for a1") {
  4120  						t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"", erres)
  4121  					}
  4122  				},
  4123  				disconnect: false,
  4124  			}})
  4125  	})
  4126  }
  4127  
  4128  func formatConfig(depth int, showGlobals, showRegisters bool, goroutineFilters string, showPprofLabels []string, hideSystemGoroutines bool, substitutePath [][2]string) string {
  4129  	formatStr := `stackTraceDepth	%d
  4130  showGlobalVariables	%v
  4131  showRegisters	%v
  4132  goroutineFilters	%q
  4133  showPprofLabels	%v
  4134  hideSystemGoroutines	%v
  4135  substitutePath	%v
  4136  `
  4137  	return fmt.Sprintf(formatStr, depth, showGlobals, showRegisters, goroutineFilters, showPprofLabels, hideSystemGoroutines, substitutePath)
  4138  }
  4139  
  4140  func TestEvaluateCommandRequest(t *testing.T) {
  4141  	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {
  4142  		runDebugSessionWithBPs(t, client, "launch",
  4143  			// Launch
  4144  			func() {
  4145  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  4146  			},
  4147  			fixture.Source, []int{}, // Breakpoint set in the program
  4148  			[]onBreakpoint{{ // Stop at first breakpoint
  4149  				execute: func() {
  4150  					checkStop(t, client, 1, "main.foobar", []int{65, 66})
  4151  
  4152  					// Request help.
  4153  					const dlvHelp = `The following commands are available:
  4154      dlv help (alias: h) 	 Prints the help message.
  4155      dlv config 	 Changes configuration parameters.
  4156      dlv sources (alias: s) 	 Print list of source files.
  4157  
  4158  Type 'dlv help' followed by a command for full documentation.
  4159  `
  4160  					client.EvaluateRequest("dlv help", 1000, "repl")
  4161  					got := client.ExpectEvaluateResponse(t)
  4162  					checkEval(t, got, dlvHelp, noChildren)
  4163  
  4164  					client.EvaluateRequest("dlv help config", 1000, "repl")
  4165  					got = client.ExpectEvaluateResponse(t)
  4166  					checkEval(t, got, msgConfig, noChildren)
  4167  
  4168  					// Test config.
  4169  					client.EvaluateRequest("dlv config", 1000, "repl")
  4170  					client.ExpectErrorResponse(t)
  4171  
  4172  					client.EvaluateRequest("dlv config -list", 1000, "repl")
  4173  					got = client.ExpectEvaluateResponse(t)
  4174  					checkEval(t, got, formatConfig(50, false, false, "", []string{}, false, [][2]string{}), noChildren)
  4175  
  4176  					// Read and modify showGlobalVariables.
  4177  					client.EvaluateRequest("dlv config -list showGlobalVariables", 1000, "repl")
  4178  					got = client.ExpectEvaluateResponse(t)
  4179  					checkEval(t, got, "showGlobalVariables\tfalse\n", noChildren)
  4180  
  4181  					client.ScopesRequest(1000)
  4182  					scopes := client.ExpectScopesResponse(t)
  4183  					if len(scopes.Body.Scopes) > 1 {
  4184  						t.Errorf("\ngot  %#v\nwant len(scopes)=1 (Locals)", scopes)
  4185  					}
  4186  					checkScope(t, scopes, 0, "Locals", -1)
  4187  
  4188  					client.EvaluateRequest("dlv config showGlobalVariables true", 1000, "repl")
  4189  					client.ExpectInvalidatedEvent(t)
  4190  					got = client.ExpectEvaluateResponse(t)
  4191  					checkEval(t, got, "showGlobalVariables\ttrue\n\nUpdated", noChildren)
  4192  
  4193  					client.EvaluateRequest("dlv config -list", 1000, "repl")
  4194  					got = client.ExpectEvaluateResponse(t)
  4195  					checkEval(t, got, formatConfig(50, true, false, "", []string{}, false, [][2]string{}), noChildren)
  4196  
  4197  					client.ScopesRequest(1000)
  4198  					scopes = client.ExpectScopesResponse(t)
  4199  					if len(scopes.Body.Scopes) < 2 {
  4200  						t.Errorf("\ngot  %#v\nwant len(scopes)=2 (Locals & Globals)", scopes)
  4201  					}
  4202  					checkScope(t, scopes, 0, "Locals", -1)
  4203  					checkScope(t, scopes, 1, "Globals (package main)", -1)
  4204  
  4205  					// Read and modify substitutePath.
  4206  					client.EvaluateRequest("dlv config -list substitutePath", 1000, "repl")
  4207  					got = client.ExpectEvaluateResponse(t)
  4208  					checkEval(t, got, "substitutePath\t[]\n", noChildren)
  4209  
  4210  					client.EvaluateRequest(fmt.Sprintf("dlv config substitutePath %q %q", "my/client/path", "your/server/path"), 1000, "repl")
  4211  					got = client.ExpectEvaluateResponse(t)
  4212  					checkEval(t, got, "substitutePath\t[[my/client/path your/server/path]]\n\nUpdated", noChildren)
  4213  
  4214  					client.EvaluateRequest(fmt.Sprintf("dlv config substitutePath %q %q", "my/client/path", "new/your/server/path"), 1000, "repl")
  4215  					got = client.ExpectEvaluateResponse(t)
  4216  					checkEval(t, got, "substitutePath\t[[my/client/path new/your/server/path]]\n\nUpdated", noChildren)
  4217  
  4218  					client.EvaluateRequest(fmt.Sprintf("dlv config substitutePath %q", "my/client/path"), 1000, "repl")
  4219  					got = client.ExpectEvaluateResponse(t)
  4220  					checkEval(t, got, "substitutePath\t[]\n\nUpdated", noChildren)
  4221  
  4222  					// Test sources.
  4223  					client.EvaluateRequest("dlv sources", 1000, "repl")
  4224  					got = client.ExpectEvaluateResponse(t)
  4225  					if !strings.Contains(got.Body.Result, fixture.Source) {
  4226  						t.Errorf("\ngot: %#v, want sources contains %s", got, fixture.Source)
  4227  					}
  4228  
  4229  					client.EvaluateRequest(fmt.Sprintf("dlv sources .*%s", strings.ReplaceAll(filepath.Base(fixture.Source), ".", "\\.")), 1000, "repl")
  4230  					got = client.ExpectEvaluateResponse(t)
  4231  					if got.Body.Result != fixture.Source {
  4232  						t.Errorf("\ngot: %#v, want sources=%q", got, fixture.Source)
  4233  					}
  4234  
  4235  					client.EvaluateRequest("dlv sources nonexistentsource", 1000, "repl")
  4236  					got = client.ExpectEvaluateResponse(t)
  4237  					if got.Body.Result != "" {
  4238  						t.Errorf("\ngot: %#v, want sources=\"\"", got)
  4239  					}
  4240  
  4241  					// Test bad inputs.
  4242  					client.EvaluateRequest("dlv help bad", 1000, "repl")
  4243  					client.ExpectErrorResponse(t)
  4244  
  4245  					client.EvaluateRequest("dlv bad", 1000, "repl")
  4246  					client.ExpectErrorResponse(t)
  4247  				},
  4248  				disconnect: true,
  4249  			}})
  4250  	})
  4251  }
  4252  
  4253  // From testvariables2 fixture
  4254  const (
  4255  	// As defined in the code
  4256  	longstr = `"very long string 0123456789a0123456789b0123456789c0123456789d0123456789e0123456789f0123456789g012345678h90123456789i0123456789j0123456789"`
  4257  	// Loaded with MaxStringLen=64
  4258  	longstrLoaded64   = `"very long string 0123456789a0123456789b0123456789c0123456789d012...+73 more"`
  4259  	longstrLoaded64re = `\"very long string 0123456789a0123456789b0123456789c0123456789d012\.\.\.\+73 more\"`
  4260  )
  4261  
  4262  // TestVariableValueTruncation tests that in certain cases
  4263  // we truncate the loaded variable values to make display more user-friendly.
  4264  func TestVariableValueTruncation(t *testing.T) {
  4265  	runTest(t, "testvariables2", func(client *daptest.Client, fixture protest.Fixture) {
  4266  		runDebugSessionWithBPs(t, client, "launch",
  4267  			// Launch
  4268  			func() {
  4269  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  4270  			},
  4271  			// Breakpoint set within the program
  4272  			fixture.Source, []int{},
  4273  			[]onBreakpoint{{
  4274  				execute: func() {
  4275  					checkStop(t, client, 1, "main.main", -1)
  4276  
  4277  					client.VariablesRequest(localsScope)
  4278  					locals := client.ExpectVariablesResponse(t)
  4279  
  4280  					// Compound variable values may be truncated
  4281  					m1Full := `map\[string\]main\.astruct \[(\"[A-Za-z]+\": {A: [0-9]+, B: [0-9]+}, )+,\.\.\.\+2 more\]`
  4282  					m1Part := `map\[string\]main\.astruct \[(\"[A-Za-z]+\": {A: [0-9]+, B: [0-9]+}, )+.+\.\.\.`
  4283  
  4284  					// In variable responses
  4285  					checkVarRegex(t, locals, -1, "m1", "m1", m1Part, `map\[string\]main\.astruct`, hasChildren)
  4286  
  4287  					// In evaluate responses (select contexts only)
  4288  					tests := []struct {
  4289  						context string
  4290  						want    string
  4291  					}{
  4292  						{"", m1Part},
  4293  						{"watch", m1Part},
  4294  						{"repl", m1Part},
  4295  						{"hover", m1Part},
  4296  						{"variables", m1Full}, // used for copy
  4297  						{"clipboard", m1Full}, // used for copy
  4298  						{"somethingelse", m1Part},
  4299  					}
  4300  					for _, tc := range tests {
  4301  						t.Run(tc.context, func(t *testing.T) {
  4302  							client.EvaluateRequest("m1", 0, tc.context)
  4303  							checkEvalRegex(t, client.ExpectEvaluateResponse(t), tc.want, hasChildren)
  4304  						})
  4305  					}
  4306  
  4307  					// Compound map keys may be truncated even further
  4308  					// As the keys are always inside of a map container,
  4309  					// this applies to variables requests only, not evaluate requests.
  4310  
  4311  					// key - compound, value - scalar (inlined key:value display) => truncate key if too long
  4312  					ref := checkVarExact(t, locals, -1, "m5", "m5", "map[main.C]int [{s: "+longstr+"}: 1, ]", "map[main.C]int", hasChildren)
  4313  					if ref > 0 {
  4314  						client.VariablesRequest(ref)
  4315  						// Key format: <truncated>... @<address>
  4316  						checkVarRegex(t, client.ExpectVariablesResponse(t), 1, `main\.C {s: "very long string 0123456789.+\.\.\. @ 0x[0-9a-f]+`, `m5\[\(\*\(\*"main\.C"\)\(0x[0-9a-f]+\)\)\]`, "1", `int`, hasChildren)
  4317  					}
  4318  					// key - scalar, value - scalar (inlined key:value display) => key not truncated
  4319  					ref = checkVarExact(t, locals, -1, "m6", "m6", "map[string]int ["+longstr+": 123, ]", "map[string]int", hasChildren)
  4320  					if ref > 0 {
  4321  						client.VariablesRequest(ref)
  4322  						checkVarExact(t, client.ExpectVariablesResponse(t), 1, longstr, `m6[`+longstr+`]`, "123", "string: int", noChildren)
  4323  					}
  4324  					// key - compound, value - compound (array-like display) => key not truncated
  4325  					ref = checkVarExact(t, locals, -1, "m7", "m7", "map[main.C]main.C [{s: "+longstr+"}: {s: \"hello\"}, ]", "map[main.C]main.C", hasChildren)
  4326  					if ref > 0 {
  4327  						client.VariablesRequest(ref)
  4328  						m7 := client.ExpectVariablesResponse(t)
  4329  						checkVarRegex(t, m7, 1, "[key 0]", `\(\*\(\*\"main\.C\"\)\(0x[0-9a-f]+\)\)`, `main\.C {s: `+longstr+`}`, `main\.C`, hasChildren)
  4330  					}
  4331  				},
  4332  				disconnect: true,
  4333  			}})
  4334  	})
  4335  }
  4336  
  4337  // TestVariableLoadingOfLongStrings tests that different string loading limits
  4338  // apply that depending on the context.
  4339  func TestVariableLoadingOfLongStrings(t *testing.T) {
  4340  	protest.MustSupportFunctionCalls(t, testBackend)
  4341  	runTest(t, "longstrings", func(client *daptest.Client, fixture protest.Fixture) {
  4342  		runDebugSessionWithBPs(t, client, "launch",
  4343  			// Launch
  4344  			func() {
  4345  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  4346  			},
  4347  			// Breakpoint set within the program
  4348  			fixture.Source, []int{},
  4349  			[]onBreakpoint{{
  4350  				execute: func() {
  4351  					checkStop(t, client, 1, "main.main", -1)
  4352  
  4353  					client.VariablesRequest(localsScope)
  4354  					locals := client.ExpectVariablesResponse(t)
  4355  
  4356  					// Limits vary for evaluate requests with different contexts
  4357  					tests := []struct {
  4358  						context string
  4359  						limit   int
  4360  					}{
  4361  						{"", DefaultLoadConfig.MaxStringLen},
  4362  						{"watch", DefaultLoadConfig.MaxStringLen},
  4363  						{"repl", maxSingleStringLen},
  4364  						{"hover", maxSingleStringLen},
  4365  						{"variables", maxSingleStringLen},
  4366  						{"clipboard", maxSingleStringLen},
  4367  						{"somethingelse", DefaultLoadConfig.MaxStringLen},
  4368  					}
  4369  					for _, tc := range tests {
  4370  						t.Run(tc.context, func(t *testing.T) {
  4371  							// Long string by itself (limits vary)
  4372  							client.EvaluateRequest("s4097", 0, tc.context)
  4373  							want := fmt.Sprintf(`"x+\.\.\.\+%d more"`, 4097-tc.limit)
  4374  							checkEvalRegex(t, client.ExpectEvaluateResponse(t), want, noChildren)
  4375  
  4376  							// Evaluated container variables return values with minimally loaded
  4377  							// strings, which are further truncated for displaying, so we
  4378  							// can't test for loading limit except in contexts where an untruncated
  4379  							// value is returned.
  4380  							client.EvaluateRequest("&s4097", 0, tc.context)
  4381  							switch tc.context {
  4382  							case "variables", "clipboard":
  4383  								want = fmt.Sprintf(`\*"x+\.\.\.\+%d more`, 4097-DefaultLoadConfig.MaxStringLen)
  4384  							default:
  4385  								want = fmt.Sprintf(`\*"x{%d}\.\.\.`, maxVarValueLen-2)
  4386  							}
  4387  							checkEvalRegex(t, client.ExpectEvaluateResponse(t), want, hasChildren)
  4388  						})
  4389  					}
  4390  
  4391  					// Long strings returned from calls are subject to a different limit,
  4392  					// same limit regardless of context
  4393  					for _, context := range []string{"", "watch", "repl", "variables", "hover", "clipboard", "somethingelse"} {
  4394  						t.Run(context, func(t *testing.T) {
  4395  							client.EvaluateRequest(`call buildString(4097)`, 1000, context)
  4396  							want := fmt.Sprintf(`"x+\.\.\.\+%d more"`, 4097-maxStringLenInCallRetVars)
  4397  							got := client.ExpectEvaluateResponse(t)
  4398  							checkEvalRegex(t, got, want, hasChildren)
  4399  						})
  4400  					}
  4401  
  4402  					// Variables requests use the most conservative loading limit
  4403  					checkVarRegex(t, locals, -1, "s513", "s513", `"x{512}\.\.\.\+1 more"`, "string", noChildren)
  4404  					// Container variables are subject to additional stricter value truncation that drops +more part
  4405  					checkVarRegex(t, locals, -1, "nested", "nested", `map\[int\]string \[513: \"x+\.\.\.`, "string", hasChildren)
  4406  				},
  4407  				disconnect: true,
  4408  			}})
  4409  	})
  4410  }
  4411  
  4412  func TestEvaluateCallRequest(t *testing.T) {
  4413  	protest.MustSupportFunctionCalls(t, testBackend)
  4414  	runTest(t, "fncall", func(client *daptest.Client, fixture protest.Fixture) {
  4415  		runDebugSessionWithBPs(t, client, "launch",
  4416  			// Launch
  4417  			func() {
  4418  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  4419  			},
  4420  			fixture.Source, []string{"main.makeclos:2"},
  4421  			[]onBreakpoint{{ // Stop in makeclos()
  4422  				execute: func() {
  4423  					checkStop(t, client, 1, "main.makeclos", -1)
  4424  
  4425  					// Topmost frame: both types of expressions should work
  4426  					client.EvaluateRequest("callstacktrace", 1000, "this context will be ignored")
  4427  					client.ExpectEvaluateResponse(t)
  4428  					client.EvaluateRequest("call callstacktrace()", 1000, "this context will be ignored")
  4429  					client.ExpectEvaluateResponse(t)
  4430  
  4431  					// Next frame: only non-call expressions will work
  4432  					client.EvaluateRequest("callstacktrace", 1001, "this context will be ignored")
  4433  					client.ExpectEvaluateResponse(t)
  4434  					client.EvaluateRequest("call callstacktrace()", 1001, "not watch")
  4435  					erres := client.ExpectVisibleErrorResponse(t)
  4436  					if !checkErrorMessageFormat(erres.Body.Error, "Unable to evaluate expression: call is only supported with topmost stack frame") {
  4437  						t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: call is only supported with topmost stack frame\"", erres)
  4438  					}
  4439  
  4440  					// A call can stop on a breakpoint
  4441  					client.EvaluateRequest("call callbreak()", 1000, "not watch")
  4442  					s := client.ExpectStoppedEvent(t)
  4443  					if s.Body.Reason != "hardcoded breakpoint" {
  4444  						t.Errorf("\ngot %#v\nwant Reason=\"hardcoded breakpoint\"", s)
  4445  					}
  4446  					erres = client.ExpectVisibleErrorResponse(t)
  4447  					if !checkErrorMessageFormat(erres.Body.Error, "Unable to evaluate expression: call stopped") {
  4448  						t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: call stopped\"", erres)
  4449  					}
  4450  
  4451  					// A call during a call causes an error
  4452  					client.EvaluateRequest("call callstacktrace()", 1000, "not watch")
  4453  					erres = client.ExpectVisibleErrorResponse(t)
  4454  					if !checkErrorMessageFormat(erres.Body.Error, "Unable to evaluate expression: cannot call function while another function call is already in progress") {
  4455  						t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: cannot call function while another function call is already in progress\"", erres)
  4456  					}
  4457  
  4458  					// Complete the call and get back to original breakpoint in makeclos()
  4459  					client.ContinueRequest(1)
  4460  					client.ExpectContinueResponse(t)
  4461  					client.ExpectStoppedEvent(t)
  4462  					checkStop(t, client, 1, "main.makeclos", -1)
  4463  
  4464  					// Inject a call for the same function that is stopped at breakpoint:
  4465  					// it might stop at the exact same breakpoint on the same goroutine,
  4466  					// but we should still detect that its an injected call that stopped
  4467  					// and not the return to the original point of injection after it
  4468  					// completed.
  4469  					client.EvaluateRequest("call makeclos(nil)", 1000, "not watch")
  4470  					stopped := client.ExpectStoppedEvent(t)
  4471  					erres = client.ExpectVisibleErrorResponse(t)
  4472  					if !checkErrorMessageFormat(erres.Body.Error, "Unable to evaluate expression: call stopped") {
  4473  						t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: call stopped\"", erres)
  4474  					}
  4475  					checkStop(t, client, stopped.Body.ThreadId, "main.makeclos", -1)
  4476  
  4477  					// Complete the call and get back to original breakpoint in makeclos()
  4478  					client.ContinueRequest(1)
  4479  					client.ExpectContinueResponse(t)
  4480  					client.ExpectStoppedEvent(t)
  4481  					checkStop(t, client, 1, "main.makeclos", -1)
  4482  				},
  4483  				disconnect: false,
  4484  			}, { // Stop at runtime breakpoint
  4485  				execute: func() {
  4486  					checkStop(t, client, 1, "main.main", -1)
  4487  
  4488  					// No return values
  4489  					client.EvaluateRequest("call call0(1, 2)", 1000, "this context will be ignored")
  4490  					got := client.ExpectEvaluateResponse(t)
  4491  					checkEval(t, got, "", noChildren)
  4492  					// One unnamed return value
  4493  					client.EvaluateRequest("call call1(one, two)", 1000, "this context will be ignored")
  4494  					got = client.ExpectEvaluateResponse(t)
  4495  					ref := checkEval(t, got, "3", hasChildren)
  4496  					if ref > 0 {
  4497  						client.VariablesRequest(ref)
  4498  						rv := client.ExpectVariablesResponse(t)
  4499  						checkChildren(t, rv, "rv", 1)
  4500  						checkVarExact(t, rv, 0, "~r2", "", "3", "int", noChildren)
  4501  					}
  4502  					// One named return value
  4503  					// Panic doesn't panic, but instead returns the error as a named return variable
  4504  					client.EvaluateRequest("call callpanic()", 1000, "this context will be ignored")
  4505  					got = client.ExpectEvaluateResponse(t)
  4506  					ref = checkEval(t, got, `interface {}(string) "callpanic panicked"`, hasChildren)
  4507  					if ref > 0 {
  4508  						client.VariablesRequest(ref)
  4509  						rv := client.ExpectVariablesResponse(t)
  4510  						checkChildren(t, rv, "rv", 1)
  4511  						ref = checkVarExact(t, rv, 0, "~panic", "", `interface {}(string) "callpanic panicked"`, "interface {}(string)", hasChildren)
  4512  						if ref > 0 {
  4513  							client.VariablesRequest(ref)
  4514  							p := client.ExpectVariablesResponse(t)
  4515  							checkChildren(t, p, "~panic", 1)
  4516  							checkVarExact(t, p, 0, "data", "", "\"callpanic panicked\"", "string", noChildren)
  4517  						}
  4518  					}
  4519  					// Multiple return values
  4520  					client.EvaluateRequest("call call2(one, two)", 1000, "this context will be ignored")
  4521  					got = client.ExpectEvaluateResponse(t)
  4522  					ref = checkEval(t, got, "1, 2", hasChildren)
  4523  					if ref > 0 {
  4524  						client.VariablesRequest(ref)
  4525  						rvs := client.ExpectVariablesResponse(t)
  4526  						checkChildren(t, rvs, "rvs", 2)
  4527  						checkVarExact(t, rvs, 0, "~r2", "", "1", "int", noChildren)
  4528  						checkVarExact(t, rvs, 1, "~r3", "", "2", "int", noChildren)
  4529  					}
  4530  					// No frame defaults to top-most frame
  4531  					client.EvaluateRequest("call call1(one, two)", 0, "this context will be ignored")
  4532  					got = client.ExpectEvaluateResponse(t)
  4533  					checkEval(t, got, "3", hasChildren)
  4534  					// Extra spaces don't matter
  4535  					client.EvaluateRequest(" call  call1(one, one) ", 0, "this context will be ignored")
  4536  					got = client.ExpectEvaluateResponse(t)
  4537  					checkEval(t, got, "2", hasChildren)
  4538  					// Just 'call', even with extra space, is treated as {expression}
  4539  					client.EvaluateRequest("call ", 1000, "watch")
  4540  					got = client.ExpectEvaluateResponse(t)
  4541  					checkEval(t, got, "\"this is a variable named `call`\"", noChildren)
  4542  
  4543  					// Call error
  4544  					client.EvaluateRequest("call call1(one)", 1000, "watch")
  4545  					erres := client.ExpectInvisibleErrorResponse(t)
  4546  					if !checkErrorMessageFormat(erres.Body.Error, "Unable to evaluate expression: not enough arguments") {
  4547  						t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: not enough arguments\"", erres)
  4548  					}
  4549  
  4550  					// Assignment - expect no error, but no return value.
  4551  					client.EvaluateRequest("call one = two", 1000, "this context will be ignored")
  4552  					got = client.ExpectEvaluateResponse(t)
  4553  					checkEval(t, got, "", noChildren)
  4554  					// Check one=two was applied.
  4555  					client.EvaluateRequest("one", 1000, "repl")
  4556  					got = client.ExpectEvaluateResponse(t)
  4557  					checkEval(t, got, "2", noChildren)
  4558  
  4559  					// Call can exit.
  4560  					client.EvaluateRequest("call callexit()", 1000, "this context will be ignored")
  4561  					client.ExpectTerminatedEvent(t)
  4562  					if res := client.ExpectVisibleErrorResponse(t); res.Body.Error == nil || !strings.Contains(res.Body.Error.Format, "terminated") {
  4563  						t.Errorf("\ngot %#v\nwant Format=.*terminated.*", res)
  4564  					}
  4565  				},
  4566  				terminated: true,
  4567  				disconnect: true,
  4568  			}})
  4569  	})
  4570  }
  4571  
  4572  func TestNextAndStep(t *testing.T) {
  4573  	runTest(t, "testinline", func(client *daptest.Client, fixture protest.Fixture) {
  4574  		runDebugSessionWithBPs(t, client, "launch",
  4575  			// Launch
  4576  			func() {
  4577  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  4578  			},
  4579  			// Set breakpoints
  4580  			fixture.Source, []int{11},
  4581  			[]onBreakpoint{{ // Stop at line 11
  4582  				execute: func() {
  4583  					checkStop(t, client, 1, "main.initialize", 11)
  4584  
  4585  					expectStop := func(fun string, line int) {
  4586  						t.Helper()
  4587  						se := client.ExpectStoppedEvent(t)
  4588  						if se.Body.Reason != "step" || se.Body.ThreadId != 1 || !se.Body.AllThreadsStopped {
  4589  							t.Errorf("got %#v, want Reason=\"step\", ThreadId=1, AllThreadsStopped=true", se)
  4590  						}
  4591  						checkStop(t, client, 1, fun, line)
  4592  					}
  4593  
  4594  					client.StepOutRequest(1)
  4595  					client.ExpectStepOutResponse(t)
  4596  					expectStop("main.main", 18)
  4597  
  4598  					client.NextRequest(1)
  4599  					client.ExpectNextResponse(t)
  4600  					expectStop("main.main", 19)
  4601  
  4602  					client.StepInRequest(1)
  4603  					client.ExpectStepInResponse(t)
  4604  					expectStop("main.inlineThis", 5)
  4605  
  4606  					client.NextRequest(-1000)
  4607  					client.ExpectNextResponse(t)
  4608  					if se := client.ExpectStoppedEvent(t); se.Body.Reason != "error" || se.Body.Text != "unknown goroutine -1000" {
  4609  						t.Errorf("got %#v, want Reason=\"error\", Text=\"unknown goroutine -1000\"", se)
  4610  					}
  4611  					checkStop(t, client, 1, "main.inlineThis", 5)
  4612  				},
  4613  				disconnect: false,
  4614  			}})
  4615  	})
  4616  }
  4617  
  4618  func TestHardCodedBreakpoints(t *testing.T) {
  4619  	runTest(t, "consts", func(client *daptest.Client, fixture protest.Fixture) {
  4620  		runDebugSessionWithBPs(t, client, "launch",
  4621  			// Launch
  4622  			func() {
  4623  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  4624  			},
  4625  			fixture.Source, []int{28},
  4626  			[]onBreakpoint{{ // Stop at line 28
  4627  				execute: func() {
  4628  					checkStop(t, client, 1, "main.main", 28)
  4629  
  4630  					client.ContinueRequest(1)
  4631  					client.ExpectContinueResponse(t)
  4632  					se := client.ExpectStoppedEvent(t)
  4633  					if se.Body.ThreadId != 1 || se.Body.Reason != "breakpoint" {
  4634  						t.Errorf("\ngot  %#v\nwant ThreadId=1 Reason=\"breakpoint\"", se)
  4635  					}
  4636  				},
  4637  				disconnect: false,
  4638  			}})
  4639  	})
  4640  }
  4641  
  4642  // TestStepInstruction executes to a breakpoint and tests stepping
  4643  // a single instruction
  4644  func TestStepInstruction(t *testing.T) {
  4645  	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {
  4646  		runDebugSessionWithBPs(t, client, "launch",
  4647  			// Launch
  4648  			func() {
  4649  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  4650  			},
  4651  			// Set breakpoints
  4652  			fixture.Source, []int{32}, // b main.foobar
  4653  			[]onBreakpoint{{
  4654  				execute: func() {
  4655  					checkStop(t, client, 1, "main.foobar", 32)
  4656  
  4657  					pc, err := getPC(t, client, 1)
  4658  					if err != nil {
  4659  						t.Fatal(err)
  4660  					}
  4661  
  4662  					// The exact instructions may change due to compiler changes,
  4663  					// but we want to make sure that all of our instructions are
  4664  					// instantiating variables, since these should not include
  4665  					// jumps.
  4666  					verifyExpectedLocation := func() {
  4667  						client.StackTraceRequest(1, 0, 20)
  4668  						st := client.ExpectStackTraceResponse(t)
  4669  						if len(st.Body.StackFrames) < 1 {
  4670  							t.Errorf("\ngot  %#v\nwant len(stackframes) => 1", st)
  4671  						} else {
  4672  							// There is a hardcoded breakpoint on line 32. All of the
  4673  							// steps should be completed before that line.
  4674  							if st.Body.StackFrames[0].Line < 32 {
  4675  								t.Errorf("\ngot  %#v\nwant Line<32", st)
  4676  							}
  4677  							if st.Body.StackFrames[0].Name != "main.foobar" {
  4678  								t.Errorf("\ngot  %#v\nwant Name=\"main.foobar\"", st)
  4679  							}
  4680  						}
  4681  					}
  4682  
  4683  					// Next instruction.
  4684  					client.NextInstructionRequest(1)
  4685  					client.ExpectNextResponse(t)
  4686  					se := client.ExpectStoppedEvent(t)
  4687  					if se.Body.ThreadId != 1 || se.Body.Reason != "step" {
  4688  						t.Errorf("\ngot  %#v\nwant ThreadId=1 Reason=\"step\"", se)
  4689  					}
  4690  					verifyExpectedLocation()
  4691  					nextPC, err := getPC(t, client, 1)
  4692  					if err != nil {
  4693  						t.Fatal(err)
  4694  					}
  4695  					if nextPC <= pc {
  4696  						t.Errorf("got %#x, expected InstructionPointerReference>%#x", nextPC, pc)
  4697  					}
  4698  
  4699  					// StepIn instruction.
  4700  					pc = nextPC
  4701  					client.StepInInstructionRequest(1)
  4702  					client.ExpectStepInResponse(t)
  4703  					se = client.ExpectStoppedEvent(t)
  4704  					if se.Body.ThreadId != 1 || se.Body.Reason != "step" {
  4705  						t.Errorf("\ngot  %#v\nwant ThreadId=1 Reason=\"step\"", se)
  4706  					}
  4707  					verifyExpectedLocation()
  4708  					nextPC, err = getPC(t, client, 1)
  4709  					if err != nil {
  4710  						t.Fatal(err)
  4711  					}
  4712  					if nextPC <= pc {
  4713  						t.Errorf("got %#x, expected InstructionPointerReference>%#x", nextPC, pc)
  4714  					}
  4715  
  4716  					// StepOut Instruction.
  4717  					pc = nextPC
  4718  					client.StepOutInstructionRequest(1)
  4719  					client.ExpectStepOutResponse(t)
  4720  					se = client.ExpectStoppedEvent(t)
  4721  					if se.Body.ThreadId != 1 || se.Body.Reason != "step" {
  4722  						t.Errorf("\ngot  %#v\nwant ThreadId=1 Reason=\"step\"", se)
  4723  					}
  4724  					verifyExpectedLocation()
  4725  					nextPC, err = getPC(t, client, 1)
  4726  					if err != nil {
  4727  						t.Fatal(err)
  4728  					}
  4729  					if nextPC <= pc {
  4730  						t.Errorf("got %#x, expected InstructionPointerReference>%#x", nextPC, pc)
  4731  					}
  4732  				},
  4733  				disconnect: true,
  4734  			}})
  4735  	})
  4736  }
  4737  
  4738  func getPC(t *testing.T, client *daptest.Client, threadId int) (uint64, error) {
  4739  	client.StackTraceRequest(threadId, 0, 1)
  4740  	st := client.ExpectStackTraceResponse(t)
  4741  	if len(st.Body.StackFrames) < 1 {
  4742  		t.Fatalf("\ngot  %#v\nwant len(stackframes) => 1", st)
  4743  	}
  4744  	return strconv.ParseUint(st.Body.StackFrames[0].InstructionPointerReference, 0, 64)
  4745  }
  4746  
  4747  // TestNextParked tests that we can switched selected goroutine to a parked one
  4748  // and perform next operation on it.
  4749  func TestNextParked(t *testing.T) {
  4750  	runTest(t, "parallel_next", func(client *daptest.Client, fixture protest.Fixture) {
  4751  		runDebugSessionWithBPs(t, client, "launch",
  4752  			// Launch
  4753  			func() {
  4754  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  4755  			},
  4756  			// Set breakpoints
  4757  			fixture.Source, []int{15},
  4758  			[]onBreakpoint{{ // Stop at line 15
  4759  				execute: func() {
  4760  					if parkedGoid := testNextParkedHelper(t, client, fixture); parkedGoid >= 0 {
  4761  						client.NextRequest(parkedGoid)
  4762  						client.ExpectNextResponse(t)
  4763  
  4764  						se := client.ExpectStoppedEvent(t)
  4765  						if se.Body.ThreadId != parkedGoid {
  4766  							t.Fatalf("Next did not continue on the newly selected goroutine, expected %d got %d", parkedGoid, se.Body.ThreadId)
  4767  						}
  4768  					}
  4769  				},
  4770  				// Let the test harness continue to process termination
  4771  				// if it hasn't gotten there already.
  4772  				disconnect: false,
  4773  			}})
  4774  	})
  4775  }
  4776  
  4777  // Finds a goroutine other than the selected one that is parked inside of main.sayhi and therefore
  4778  // still has a line to execute if resumed with next.
  4779  func testNextParkedHelper(t *testing.T, client *daptest.Client, fixture protest.Fixture) int {
  4780  	t.Helper()
  4781  	// Set a breakpoint at main.sayhi
  4782  	client.SetBreakpointsRequest(fixture.Source, []int{8})
  4783  	client.ExpectSetBreakpointsResponse(t)
  4784  
  4785  	parkedGoid := -1
  4786  	for parkedGoid < 0 {
  4787  		client.ContinueRequest(1)
  4788  		client.ExpectContinueResponse(t)
  4789  		event := client.ExpectMessage(t)
  4790  		switch event.(type) {
  4791  		case *dap.StoppedEvent:
  4792  			// ok
  4793  		case *dap.TerminatedEvent:
  4794  			// This is very unlikely to happen. But in theory if all sayhi
  4795  			// goroutines are run serially, there will never be a second parked
  4796  			// sayhi goroutine when another breaks and we will keep trying
  4797  			// until process termination.
  4798  			return -1
  4799  		}
  4800  
  4801  		se := event.(*dap.StoppedEvent)
  4802  
  4803  		client.ThreadsRequest()
  4804  		threads := client.ExpectThreadsResponse(t)
  4805  
  4806  		// Search for a parked goroutine that we know for sure will have to be
  4807  		// resumed before the program can exit. This is a parked goroutine that:
  4808  		// 1. is executing main.sayhi
  4809  		// 2. hasn't called wg.Done yet
  4810  		// 3. is not the currently selected goroutine
  4811  		for _, g := range threads.Body.Threads {
  4812  			if g.Id == se.Body.ThreadId || g.Id == 0 {
  4813  				// Skip selected goroutine and goroutine 0
  4814  				continue
  4815  			}
  4816  			client.StackTraceRequest(g.Id, 0, 5)
  4817  			frames := client.ExpectStackTraceResponse(t)
  4818  			for _, frame := range frames.Body.StackFrames {
  4819  				// line 11 is the line where wg.Done is called
  4820  				if frame.Name == "main.sayhi" && frame.Line < 11 {
  4821  					parkedGoid = g.Id
  4822  					break
  4823  				}
  4824  			}
  4825  			if parkedGoid >= 0 {
  4826  				break
  4827  			}
  4828  		}
  4829  	}
  4830  
  4831  	// Clear all breakpoints.
  4832  	client.SetBreakpointsRequest(fixture.Source, []int{})
  4833  	client.ExpectSetBreakpointsResponse(t)
  4834  	return parkedGoid
  4835  }
  4836  
  4837  // TestStepOutPreservesGoroutine is inspired by proc_test.TestStepOutPreservesGoroutine
  4838  // and checks that StepOut preserves the currently selected goroutine.
  4839  func TestStepOutPreservesGoroutine(t *testing.T) {
  4840  	// Checks that StepOut preserves the currently selected goroutine.
  4841  	rand.Seed(time.Now().Unix())
  4842  	runTest(t, "issue2113", func(client *daptest.Client, fixture protest.Fixture) {
  4843  		runDebugSessionWithBPs(t, client, "launch",
  4844  			// Launch
  4845  			func() {
  4846  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  4847  			},
  4848  			// Set breakpoints
  4849  			fixture.Source, []int{25},
  4850  			[]onBreakpoint{{ // Stop at line 25
  4851  				execute: func() {
  4852  					client.ContinueRequest(1)
  4853  					client.ExpectContinueResponse(t)
  4854  
  4855  					// The program contains runtime.Breakpoint()
  4856  					se := client.ExpectStoppedEvent(t)
  4857  
  4858  					client.ThreadsRequest()
  4859  					gs := client.ExpectThreadsResponse(t)
  4860  
  4861  					candg := []int{}
  4862  					bestg := []int{}
  4863  					for _, g := range gs.Body.Threads {
  4864  						// We do not need to check the thread that the program
  4865  						// is currently stopped on.
  4866  						if g.Id == se.Body.ThreadId {
  4867  							continue
  4868  						}
  4869  
  4870  						client.StackTraceRequest(g.Id, 0, 20)
  4871  						frames := client.ExpectStackTraceResponse(t)
  4872  						for _, frame := range frames.Body.StackFrames {
  4873  							if frame.Name == "main.coroutine" {
  4874  								candg = append(candg, g.Id)
  4875  								if strings.HasPrefix(frames.Body.StackFrames[0].Name, "runtime.") {
  4876  									bestg = append(bestg, g.Id)
  4877  								}
  4878  								break
  4879  							}
  4880  						}
  4881  					}
  4882  					var goroutineId int
  4883  					if len(bestg) > 0 {
  4884  						goroutineId = bestg[rand.Intn(len(bestg))]
  4885  						t.Logf("selected goroutine %d (best)\n", goroutineId)
  4886  					} else if len(candg) > 0 {
  4887  						goroutineId = candg[rand.Intn(len(candg))]
  4888  						t.Logf("selected goroutine %d\n", goroutineId)
  4889  
  4890  					}
  4891  
  4892  					if goroutineId != 0 {
  4893  						client.StepOutRequest(goroutineId)
  4894  						client.ExpectStepOutResponse(t)
  4895  					} else {
  4896  						client.ContinueRequest(-1)
  4897  						client.ExpectContinueResponse(t)
  4898  					}
  4899  
  4900  					switch e := client.ExpectMessage(t).(type) {
  4901  					case *dap.StoppedEvent:
  4902  						if e.Body.ThreadId != goroutineId {
  4903  							t.Fatalf("StepOut did not continue on the selected goroutine, expected %d got %d", goroutineId, e.Body.ThreadId)
  4904  						}
  4905  					case *dap.TerminatedEvent:
  4906  						t.Logf("program terminated")
  4907  					default:
  4908  						t.Fatalf("Unexpected event type: expect stopped or terminated event, got %#v", e)
  4909  					}
  4910  				},
  4911  				disconnect: false,
  4912  			}})
  4913  	})
  4914  }
  4915  
  4916  func checkStopOnNextWhileNextingError(t *testing.T, client *daptest.Client, threadID int) {
  4917  	t.Helper()
  4918  	oe := client.ExpectOutputEvent(t)
  4919  	if oe.Body.Category != "console" || oe.Body.Output != fmt.Sprintf("invalid command: %s\n", BetterNextWhileNextingError) {
  4920  		t.Errorf("\ngot  %#v\nwant Category=\"console\" Output=\"invalid command: %s\\n\"", oe, BetterNextWhileNextingError)
  4921  	}
  4922  	se := client.ExpectStoppedEvent(t)
  4923  	if se.Body.ThreadId != threadID || se.Body.Reason != "exception" || se.Body.Description != "invalid command" || se.Body.Text != BetterNextWhileNextingError {
  4924  		t.Errorf("\ngot  %#v\nwant ThreadId=%d Reason=\"exception\" Description=\"invalid command\" Text=\"%s\"", se, threadID, BetterNextWhileNextingError)
  4925  	}
  4926  	client.ExceptionInfoRequest(1)
  4927  	eInfo := client.ExpectExceptionInfoResponse(t)
  4928  	if eInfo.Body.ExceptionId != "invalid command" || eInfo.Body.Description != BetterNextWhileNextingError {
  4929  		t.Errorf("\ngot  %#v\nwant ExceptionId=\"invalid command\" Text=\"%s\"", eInfo, BetterNextWhileNextingError)
  4930  	}
  4931  }
  4932  
  4933  func TestBadAccess(t *testing.T) {
  4934  	if runtime.GOOS != "darwin" || testBackend != "lldb" {
  4935  		t.Skip("not applicable")
  4936  	}
  4937  	runTest(t, "issue2078", func(client *daptest.Client, fixture protest.Fixture) {
  4938  		runDebugSessionWithBPs(t, client, "launch",
  4939  			// Launch
  4940  			func() {
  4941  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  4942  			},
  4943  			// Set breakpoints
  4944  			fixture.Source, []int{4},
  4945  			[]onBreakpoint{{ // Stop at line 4
  4946  				execute: func() {
  4947  					checkStop(t, client, 1, "main.main", 4)
  4948  
  4949  					expectStoppedOnError := func(errorPrefix string) {
  4950  						t.Helper()
  4951  						se := client.ExpectStoppedEvent(t)
  4952  						if se.Body.ThreadId != 1 || se.Body.Reason != "exception" || (se.Body.Description != "runtime error" && se.Body.Description != "panic") || !strings.Contains(se.Body.Text, errorPrefix) {
  4953  							t.Errorf("\ngot  %#v\nwant ThreadId=1 Reason=\"exception\" Description=\"runtime error\" Text=\"%s\"", se, errorPrefix)
  4954  						}
  4955  						client.ExceptionInfoRequest(1)
  4956  						eInfo := client.ExpectExceptionInfoResponse(t)
  4957  						if (eInfo.Body.ExceptionId != "runtime error" && eInfo.Body.ExceptionId != "panic") || !strings.Contains(eInfo.Body.Description, errorPrefix) {
  4958  							t.Errorf("\ngot  %#v\nwant ExceptionId=\"runtime error\" Text=\"%s\"", eInfo, errorPrefix)
  4959  						}
  4960  					}
  4961  
  4962  					client.ContinueRequest(1)
  4963  					client.ExpectContinueResponse(t)
  4964  					expectStoppedOnError("invalid memory address or nil pointer dereference")
  4965  
  4966  					client.StackTraceRequest(1, 0, 2)
  4967  					st := client.ExpectStackTraceResponse(t)
  4968  					if len(st.Body.StackFrames) > 0 && st.Body.StackFrames[0].Name == "runtime.fatalpanic" {
  4969  						// this debugserver has --unmask-signals and it works correctly
  4970  						return
  4971  					}
  4972  
  4973  					client.NextRequest(1)
  4974  					client.ExpectNextResponse(t)
  4975  					expectStoppedOnError("invalid memory address or nil pointer dereference")
  4976  
  4977  					client.NextRequest(1)
  4978  					client.ExpectNextResponse(t)
  4979  					checkStopOnNextWhileNextingError(t, client, 1)
  4980  
  4981  					client.StepInRequest(1)
  4982  					client.ExpectStepInResponse(t)
  4983  					checkStopOnNextWhileNextingError(t, client, 1)
  4984  
  4985  					client.StepOutRequest(1)
  4986  					client.ExpectStepOutResponse(t)
  4987  					checkStopOnNextWhileNextingError(t, client, 1)
  4988  				},
  4989  				disconnect: true,
  4990  			}})
  4991  	})
  4992  }
  4993  
  4994  // TestNextWhileNexting is inspired by command_test.TestIssue387 and tests
  4995  // that when 'next' is interrupted by a 'breakpoint', calling 'next'
  4996  // again will produce an error with a helpful message, and 'continue'
  4997  // will resume the program.
  4998  func TestNextWhileNexting(t *testing.T) {
  4999  	// a breakpoint triggering during a 'next' operation will interrupt 'next''
  5000  	// Unlike the test for the terminal package, we cannot be certain
  5001  	// of the number of breakpoints we expect to hit, since multiple
  5002  	// breakpoints being hit at the same time is not supported in DAP stopped
  5003  	// events.
  5004  	runTest(t, "issue387", func(client *daptest.Client, fixture protest.Fixture) {
  5005  		runDebugSessionWithBPs(t, client, "launch",
  5006  			// Launch
  5007  			func() {
  5008  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  5009  			},
  5010  			// Set breakpoints
  5011  			fixture.Source, []int{15},
  5012  			[]onBreakpoint{{ // Stop at line 15
  5013  				execute: func() {
  5014  					checkStop(t, client, 1, "main.main", 15)
  5015  
  5016  					client.SetBreakpointsRequest(fixture.Source, []int{8})
  5017  					client.ExpectSetBreakpointsResponse(t)
  5018  
  5019  					client.ContinueRequest(1)
  5020  					client.ExpectContinueResponse(t)
  5021  
  5022  					bpSe := client.ExpectStoppedEvent(t)
  5023  					threadID := bpSe.Body.ThreadId
  5024  					checkStop(t, client, threadID, "main.dostuff", 8)
  5025  
  5026  					for pos := 9; pos < 11; pos++ {
  5027  						client.NextRequest(threadID)
  5028  						client.ExpectNextResponse(t)
  5029  
  5030  						stepInProgress := true
  5031  						for stepInProgress {
  5032  							m := client.ExpectStoppedEvent(t)
  5033  							switch m.Body.Reason {
  5034  							case "step":
  5035  								if !m.Body.AllThreadsStopped {
  5036  									t.Errorf("got %#v, want Reason=\"step\", AllThreadsStopped=true", m)
  5037  								}
  5038  								checkStop(t, client, m.Body.ThreadId, "main.dostuff", pos)
  5039  								stepInProgress = false
  5040  							case "breakpoint":
  5041  								if !m.Body.AllThreadsStopped {
  5042  									t.Errorf("got %#v, want Reason=\"breakpoint\", AllThreadsStopped=true", m)
  5043  								}
  5044  
  5045  								if stepInProgress {
  5046  									// We encountered a breakpoint on a different thread. We should have to resume execution
  5047  									// using continue.
  5048  									oe := client.ExpectOutputEvent(t)
  5049  									if oe.Body.Category != "console" || !strings.Contains(oe.Body.Output, "Step interrupted by a breakpoint.") {
  5050  										t.Errorf("\ngot  %#v\nwant Category=\"console\" Output=\"Step interrupted by a breakpoint.\"", oe)
  5051  									}
  5052  									client.NextRequest(m.Body.ThreadId)
  5053  									client.ExpectNextResponse(t)
  5054  									checkStopOnNextWhileNextingError(t, client, m.Body.ThreadId)
  5055  									// Continue since we have not finished the step request.
  5056  									client.ContinueRequest(threadID)
  5057  									client.ExpectContinueResponse(t)
  5058  								} else {
  5059  									checkStop(t, client, m.Body.ThreadId, "main.dostuff", 8)
  5060  									// Switch to stepping on this thread instead.
  5061  									pos = 8
  5062  									threadID = m.Body.ThreadId
  5063  								}
  5064  							default:
  5065  								t.Fatalf("got %#v, want StoppedEvent on step or breakpoint", m)
  5066  							}
  5067  						}
  5068  					}
  5069  				},
  5070  				disconnect: true,
  5071  			}})
  5072  	})
  5073  }
  5074  
  5075  func TestPanicBreakpointOnContinue(t *testing.T) {
  5076  	runTest(t, "panic", func(client *daptest.Client, fixture protest.Fixture) {
  5077  		runDebugSessionWithBPs(t, client, "launch",
  5078  			// Launch
  5079  			func() {
  5080  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  5081  			},
  5082  			// Set breakpoints
  5083  			fixture.Source, []int{5},
  5084  			[]onBreakpoint{{
  5085  				execute: func() {
  5086  					checkStop(t, client, 1, "main.main", 5)
  5087  
  5088  					client.ContinueRequest(1)
  5089  					client.ExpectContinueResponse(t)
  5090  
  5091  					text := "\"BOOM!\""
  5092  					se := client.ExpectStoppedEvent(t)
  5093  					if se.Body.ThreadId != 1 || se.Body.Reason != "exception" || se.Body.Description != "panic" || se.Body.Text != text {
  5094  						t.Errorf("\ngot  %#v\nwant ThreadId=1 Reason=\"exception\" Description=\"panic\" Text=%q", se, text)
  5095  					}
  5096  
  5097  					client.ExceptionInfoRequest(1)
  5098  					eInfo := client.ExpectExceptionInfoResponse(t)
  5099  					if eInfo.Body.ExceptionId != "panic" || eInfo.Body.Description != text {
  5100  						t.Errorf("\ngot  %#v\nwant ExceptionId=\"panic\" Description=%q", eInfo, text)
  5101  					}
  5102  
  5103  					client.StackTraceRequest(se.Body.ThreadId, 0, 20)
  5104  					st := client.ExpectStackTraceResponse(t)
  5105  					for i, frame := range st.Body.StackFrames {
  5106  						if strings.HasPrefix(frame.Name, "runtime.") {
  5107  							if frame.PresentationHint != "subtle" {
  5108  								t.Errorf("\ngot Body.StackFrames[%d]=%#v\nwant Source.PresentationHint=\"subtle\"", i, frame)
  5109  							}
  5110  						} else if frame.Source != nil && frame.Source.PresentationHint != "" {
  5111  							t.Errorf("\ngot Body.StackFrames[%d]=%#v\nwant Source.PresentationHint=\"\"", i, frame)
  5112  						}
  5113  					}
  5114  				},
  5115  				disconnect: true,
  5116  			}})
  5117  	})
  5118  }
  5119  
  5120  func TestPanicBreakpointOnNext(t *testing.T) {
  5121  	if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 14) {
  5122  		// In Go 1.13, 'next' will step into the defer in the runtime
  5123  		// main function, instead of the next line in the main program.
  5124  		t.SkipNow()
  5125  	}
  5126  
  5127  	runTest(t, "panic", func(client *daptest.Client, fixture protest.Fixture) {
  5128  		runDebugSessionWithBPs(t, client, "launch",
  5129  			// Launch
  5130  			func() {
  5131  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  5132  			},
  5133  			// Set breakpoints
  5134  			fixture.Source, []int{5},
  5135  			[]onBreakpoint{{
  5136  				execute: func() {
  5137  					checkStop(t, client, 1, "main.main", 5)
  5138  
  5139  					client.NextRequest(1)
  5140  					client.ExpectNextResponse(t)
  5141  
  5142  					text := "\"BOOM!\""
  5143  					se := client.ExpectStoppedEvent(t)
  5144  					if se.Body.ThreadId != 1 || se.Body.Reason != "exception" || se.Body.Description != "panic" || se.Body.Text != text {
  5145  						t.Errorf("\ngot  %#v\nwant ThreadId=1 Reason=\"exception\" Description=\"panic\" Text=%q", se, text)
  5146  					}
  5147  
  5148  					client.ExceptionInfoRequest(1)
  5149  					eInfo := client.ExpectExceptionInfoResponse(t)
  5150  					if eInfo.Body.ExceptionId != "panic" || eInfo.Body.Description != text {
  5151  						t.Errorf("\ngot  %#v\nwant ExceptionId=\"panic\" Description=%q", eInfo, text)
  5152  					}
  5153  				},
  5154  				disconnect: true,
  5155  			}})
  5156  	})
  5157  }
  5158  
  5159  func TestFatalThrowBreakpoint(t *testing.T) {
  5160  	runTest(t, "fatalerror", func(client *daptest.Client, fixture protest.Fixture) {
  5161  		runDebugSessionWithBPs(t, client, "launch",
  5162  			// Launch
  5163  			func() {
  5164  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  5165  			},
  5166  			// Set breakpoints
  5167  			fixture.Source, []int{3},
  5168  			[]onBreakpoint{{
  5169  				execute: func() {
  5170  					checkStop(t, client, 1, "main.main", 3)
  5171  
  5172  					client.ContinueRequest(1)
  5173  					client.ExpectContinueResponse(t)
  5174  
  5175  					var text string
  5176  					// This does not work for Go 1.16.
  5177  					ver, _ := goversion.Parse(runtime.Version())
  5178  					if ver.Major != 1 || ver.Minor != 16 {
  5179  						text = "\"go of nil func value\""
  5180  					}
  5181  
  5182  					se := client.ExpectStoppedEvent(t)
  5183  					if se.Body.ThreadId != 1 || se.Body.Reason != "exception" || se.Body.Description != "fatal error" || se.Body.Text != text {
  5184  						t.Errorf("\ngot  %#v\nwant ThreadId=1 Reason=\"exception\" Description=\"fatal error\" Text=%q", se, text)
  5185  					}
  5186  
  5187  					// This does not work for Go 1.16.
  5188  					errorPrefix := text
  5189  					if errorPrefix == "" {
  5190  						errorPrefix = "Throw reason unavailable, see https://github.com/golang/go/issues/46425"
  5191  					}
  5192  					client.ExceptionInfoRequest(1)
  5193  					eInfo := client.ExpectExceptionInfoResponse(t)
  5194  					if eInfo.Body.ExceptionId != "fatal error" || !strings.HasPrefix(eInfo.Body.Description, errorPrefix) {
  5195  						t.Errorf("\ngot  %#v\nwant ExceptionId=\"runtime error\" Text=%s", eInfo, errorPrefix)
  5196  					}
  5197  				},
  5198  				disconnect: true,
  5199  			}})
  5200  	})
  5201  	runTest(t, "testdeadlock", func(client *daptest.Client, fixture protest.Fixture) {
  5202  		runDebugSessionWithBPs(t, client, "launch",
  5203  			// Launch
  5204  			func() {
  5205  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  5206  			},
  5207  			// Set breakpoints
  5208  			fixture.Source, []int{3},
  5209  			[]onBreakpoint{{
  5210  				execute: func() {
  5211  					checkStop(t, client, 1, "main.main", 3)
  5212  
  5213  					client.ContinueRequest(1)
  5214  					client.ExpectContinueResponse(t)
  5215  
  5216  					// This does not work for Go 1.16 so skip by detecting versions before or after 1.16.
  5217  					var text string
  5218  					if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 16) || goversion.VersionAfterOrEqual(runtime.Version(), 1, 17) {
  5219  						text = "\"all goroutines are asleep - deadlock!\""
  5220  					}
  5221  					se := client.ExpectStoppedEvent(t)
  5222  					if se.Body.Reason != "exception" || se.Body.Description != "fatal error" || se.Body.Text != text {
  5223  						t.Errorf("\ngot  %#v\nwant Reason=\"exception\" Description=\"fatal error\" Text=%q", se, text)
  5224  					}
  5225  
  5226  					// TODO(suzmue): Get the exception info for the thread and check the description
  5227  					// includes "all goroutines are asleep - deadlock!".
  5228  					// Stopped events with no selected goroutines need to be supported to test deadlock.
  5229  				},
  5230  				disconnect: true,
  5231  			}})
  5232  	})
  5233  }
  5234  
  5235  // checkStop covers the standard sequence of requests issued by
  5236  // a client at a breakpoint or another non-terminal stop event.
  5237  // The details have been tested by other tests,
  5238  // so this is just a sanity check.
  5239  // Skips line check if line is -1.
  5240  func checkStop(t *testing.T, client *daptest.Client, thread int, fname string, line interface{}) {
  5241  	t.Helper()
  5242  	client.ThreadsRequest()
  5243  	client.ExpectThreadsResponse(t)
  5244  
  5245  	client.CheckStopLocation(t, thread, fname, line)
  5246  
  5247  	client.ScopesRequest(1000)
  5248  	client.ExpectScopesResponse(t)
  5249  
  5250  	client.VariablesRequest(localsScope)
  5251  	client.ExpectVariablesResponse(t)
  5252  }
  5253  
  5254  // onBreakpoint specifies what the test harness should simulate at
  5255  // a stopped breakpoint. First execute() is to be called to test
  5256  // specified editor-driven or user-driven requests. Then if
  5257  // disconnect is true, the test harness will abort the program
  5258  // execution. Otherwise, a continue will be issued and the
  5259  // program will continue to the next breakpoint or termination.
  5260  // If terminated is true, we expect requests at this breakpoint
  5261  // to result in termination.
  5262  type onBreakpoint struct {
  5263  	execute    func()
  5264  	disconnect bool
  5265  	terminated bool
  5266  }
  5267  
  5268  // runDebugSessionWithBPs is a helper for executing the common init and shutdown
  5269  // sequences for a program that does not stop on entry
  5270  // while specifying breakpoints and unique launch/attach criteria via parameters.
  5271  //
  5272  //	cmd            - "launch" or "attach"
  5273  //	cmdRequest     - a function that sends a launch or attach request,
  5274  //	                 so the test author has full control of its arguments.
  5275  //	                 Note that he rest of the test sequence assumes that
  5276  //	                 stopOnEntry is false.
  5277  //	 source        - source file path, needed to set breakpoints, "" if none to be set.
  5278  //	 breakpoints   - list of lines, where breakpoints are to be set
  5279  //	 onBPs         - list of test sequences to execute at each of the set breakpoints.
  5280  func runDebugSessionWithBPs(t *testing.T, client *daptest.Client, cmd string, cmdRequest func(), source string, breakpoints interface{}, onBPs []onBreakpoint) {
  5281  	client.InitializeRequest()
  5282  	client.ExpectInitializeResponseAndCapabilities(t)
  5283  
  5284  	cmdRequest()
  5285  	client.ExpectInitializedEvent(t)
  5286  	switch cmd {
  5287  	case "launch":
  5288  		client.ExpectLaunchResponse(t)
  5289  	case "attach":
  5290  		client.ExpectAttachResponse(t)
  5291  	default:
  5292  		panic("expected launch or attach command")
  5293  	}
  5294  
  5295  	if source != "" {
  5296  		switch breakpoints := breakpoints.(type) {
  5297  		case []int:
  5298  			client.SetBreakpointsRequest(source, breakpoints)
  5299  			client.ExpectSetBreakpointsResponse(t)
  5300  		case []string:
  5301  			fbps := []dap.FunctionBreakpoint{}
  5302  			for _, bp := range breakpoints {
  5303  				fbps = append(fbps, dap.FunctionBreakpoint{Name: bp})
  5304  			}
  5305  			client.SetFunctionBreakpointsRequest(fbps)
  5306  			client.ExpectSetFunctionBreakpointsResponse(t)
  5307  		}
  5308  	}
  5309  
  5310  	// Skip no-op setExceptionBreakpoints
  5311  
  5312  	client.ConfigurationDoneRequest()
  5313  	client.ExpectConfigurationDoneResponse(t)
  5314  
  5315  	// Program automatically continues to breakpoint or completion
  5316  
  5317  	// TODO(polina): See if we can make this more like withTestProcessArgs in proc_test:
  5318  	// a single function pointer gets called here and then if it wants to continue it calls
  5319  	// client.ContinueRequest/client.ExpectContinueResponse/client.ExpectStoppedEvent
  5320  	// (possibly using a helper function).
  5321  	for _, onBP := range onBPs {
  5322  		client.ExpectStoppedEvent(t)
  5323  		onBP.execute()
  5324  		if onBP.disconnect {
  5325  			client.DisconnectRequestWithKillOption(true)
  5326  			if onBP.terminated {
  5327  				client.ExpectOutputEventProcessExitedAnyStatus(t)
  5328  				client.ExpectOutputEventDetaching(t)
  5329  			} else {
  5330  				client.ExpectOutputEventDetachingKill(t)
  5331  			}
  5332  			client.ExpectDisconnectResponse(t)
  5333  			client.ExpectTerminatedEvent(t)
  5334  			return
  5335  		}
  5336  		client.ContinueRequest(1)
  5337  		client.ExpectContinueResponse(t)
  5338  		// "Continue" is triggered after the response is sent
  5339  	}
  5340  
  5341  	if cmd == "launch" { // Let the program run to completion
  5342  		client.ExpectTerminatedEvent(t)
  5343  	}
  5344  	client.DisconnectRequestWithKillOption(true)
  5345  	if cmd == "launch" {
  5346  		client.ExpectOutputEventProcessExitedAnyStatus(t)
  5347  		client.ExpectOutputEventDetaching(t)
  5348  	} else if cmd == "attach" {
  5349  		client.ExpectOutputEventDetachingKill(t)
  5350  	}
  5351  	client.ExpectDisconnectResponse(t)
  5352  	client.ExpectTerminatedEvent(t)
  5353  }
  5354  
  5355  // runDebugSession is a helper for executing the standard init and shutdown
  5356  // sequences for a program that does not stop on entry
  5357  // while specifying unique launch criteria via parameters.
  5358  func runDebugSession(t *testing.T, client *daptest.Client, cmd string, cmdRequest func()) {
  5359  	runDebugSessionWithBPs(t, client, cmd, cmdRequest, "", nil, nil)
  5360  }
  5361  
  5362  func TestLaunchDebugRequest(t *testing.T) {
  5363  	rescueStderr := os.Stderr
  5364  	r, w, _ := os.Pipe()
  5365  	os.Stderr = w
  5366  	done := make(chan struct{})
  5367  
  5368  	var err []byte
  5369  
  5370  	go func() {
  5371  		err, _ = io.ReadAll(r)
  5372  		t.Log(string(err))
  5373  		close(done)
  5374  	}()
  5375  
  5376  	tmpBin := "__tmpBin"
  5377  	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
  5378  		// We reuse the harness that builds, but ignore the built binary,
  5379  		// only relying on the source to be built in response to LaunchRequest.
  5380  		runDebugSession(t, client, "launch", func() {
  5381  			client.LaunchRequestWithArgs(map[string]interface{}{
  5382  				"mode": "debug", "program": fixture.Source, "output": tmpBin,
  5383  			})
  5384  		})
  5385  	})
  5386  	// Wait for the test to finish to capture all stderr
  5387  	time.Sleep(100 * time.Millisecond)
  5388  
  5389  	w.Close()
  5390  	<-done
  5391  	os.Stderr = rescueStderr
  5392  
  5393  	rmErrRe := regexp.MustCompile(`could not remove .*\n`)
  5394  	rmErr := rmErrRe.FindString(string(err))
  5395  	if rmErr != "" {
  5396  		// On Windows, a file in use cannot be removed, resulting in "Access is denied".
  5397  		// When the process exits, Delve releases the binary by calling
  5398  		// BinaryInfo.Close(), but it appears that it is still in use (by Windows?)
  5399  		// shortly after. gobuild.Remove has a delay to address this, but
  5400  		// to avoid any test flakiness we guard against this failure here as well.
  5401  		if runtime.GOOS != "windows" || !stringContainsCaseInsensitive(rmErr, "Access is denied") {
  5402  			t.Fatalf("Binary removal failure:\n%s\n", rmErr)
  5403  		}
  5404  	} else {
  5405  		tmpBin = cleanExeName(tmpBin)
  5406  		// We did not get a removal error, but did we even try to remove before exiting?
  5407  		// Confirm that the binary did get removed.
  5408  		if _, err := os.Stat(tmpBin); err == nil || os.IsExist(err) {
  5409  			t.Fatal("Failed to remove temp binary", tmpBin)
  5410  		}
  5411  	}
  5412  }
  5413  
  5414  // TestLaunchRequestDefaults tests defaults for launch attribute that are explicit in other tests.
  5415  func TestLaunchRequestDefaults(t *testing.T) {
  5416  	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
  5417  		runDebugSession(t, client, "launch", func() {
  5418  			client.LaunchRequestWithArgs(map[string]interface{}{
  5419  				"mode": "" /*"debug" by default*/, "program": fixture.Source, "output": "__mybin",
  5420  			})
  5421  		})
  5422  	})
  5423  	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
  5424  		runDebugSession(t, client, "launch", func() {
  5425  			client.LaunchRequestWithArgs(map[string]interface{}{
  5426  				/*"mode":"debug" by default*/ "program": fixture.Source, "output": "__mybin",
  5427  			})
  5428  		})
  5429  	})
  5430  	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
  5431  		runDebugSession(t, client, "launch", func() {
  5432  			// Use the temporary output binary.
  5433  			client.LaunchRequestWithArgs(map[string]interface{}{
  5434  				"mode": "debug", "program": fixture.Source,
  5435  			})
  5436  		})
  5437  	})
  5438  }
  5439  
  5440  // TestLaunchRequestOutputPath verifies that relative output binary path
  5441  // is mapped to server's, not target's, working directory.
  5442  func TestLaunchRequestOutputPath(t *testing.T) {
  5443  	runTest(t, "testargs", func(client *daptest.Client, fixture protest.Fixture) {
  5444  		inrel := "__somebin"
  5445  		wd, _ := os.Getwd()
  5446  		outabs := cleanExeName(filepath.Join(wd, inrel))
  5447  		runDebugSessionWithBPs(t, client, "launch",
  5448  			// Launch
  5449  			func() {
  5450  				client.LaunchRequestWithArgs(map[string]interface{}{
  5451  					"mode": "debug", "program": fixture.Source, "output": inrel,
  5452  					"cwd": filepath.Dir(wd),
  5453  				})
  5454  			},
  5455  			// Set breakpoints
  5456  			fixture.Source, []int{12},
  5457  			[]onBreakpoint{{
  5458  				execute: func() {
  5459  					checkStop(t, client, 1, "main.main", 12)
  5460  					client.EvaluateRequest("os.Args[0]", 1000, "repl")
  5461  					checkEval(t, client.ExpectEvaluateResponse(t), fmt.Sprintf("%q", outabs), noChildren)
  5462  				},
  5463  				disconnect: true,
  5464  			}})
  5465  	})
  5466  }
  5467  
  5468  func TestExitNonZeroStatus(t *testing.T) {
  5469  	runTest(t, "pr1055", func(client *daptest.Client, fixture protest.Fixture) {
  5470  		client.InitializeRequest()
  5471  		client.ExpectInitializeResponseAndCapabilities(t)
  5472  
  5473  		client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  5474  		client.ExpectInitializedEvent(t)
  5475  		client.ExpectLaunchResponse(t)
  5476  
  5477  		client.ConfigurationDoneRequest()
  5478  		client.ExpectConfigurationDoneResponse(t)
  5479  
  5480  		client.ExpectTerminatedEvent(t)
  5481  
  5482  		client.DisconnectRequest()
  5483  		// Check that the process exit status is 2.
  5484  		oep := client.ExpectOutputEventProcessExited(t, 2)
  5485  		if oep.Body.Category != "console" {
  5486  			t.Errorf("\ngot %#v\nwant Category='console'", oep)
  5487  		}
  5488  		oed := client.ExpectOutputEventDetaching(t)
  5489  		if oed.Body.Category != "console" {
  5490  			t.Errorf("\ngot %#v\nwant Category='console'", oed)
  5491  		}
  5492  		client.ExpectDisconnectResponse(t)
  5493  		client.ExpectTerminatedEvent(t)
  5494  	})
  5495  }
  5496  
  5497  func TestNoDebug_GoodExitStatus(t *testing.T) {
  5498  	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
  5499  		runNoDebugSession(t, client, func() {
  5500  			client.LaunchRequestWithArgs(map[string]interface{}{
  5501  				"noDebug": true, "mode": "debug", "program": fixture.Source, "output": "__mybin",
  5502  			})
  5503  		}, 0)
  5504  	})
  5505  }
  5506  
  5507  func TestNoDebug_BadExitStatus(t *testing.T) {
  5508  	runTest(t, "issue1101", func(client *daptest.Client, fixture protest.Fixture) {
  5509  		runNoDebugSession(t, client, func() {
  5510  			client.LaunchRequestWithArgs(map[string]interface{}{
  5511  				"noDebug": true, "mode": "exec", "program": fixture.Path,
  5512  			})
  5513  		}, 2)
  5514  	})
  5515  }
  5516  
  5517  // runNoDebugSession tests the session started with noDebug=true runs
  5518  // to completion and logs termination status.
  5519  func runNoDebugSession(t *testing.T, client *daptest.Client, launchRequest func(), exitStatus int) {
  5520  	client.InitializeRequest()
  5521  	client.ExpectInitializeResponseAndCapabilities(t)
  5522  
  5523  	launchRequest()
  5524  	// no initialized event.
  5525  	// noDebug mode applies only to "launch" requests.
  5526  	client.ExpectLaunchResponse(t)
  5527  
  5528  	client.ExpectOutputEventProcessExited(t, exitStatus)
  5529  	client.ExpectTerminatedEvent(t)
  5530  	client.DisconnectRequestWithKillOption(true)
  5531  	client.ExpectDisconnectResponse(t)
  5532  	client.ExpectTerminatedEvent(t)
  5533  }
  5534  
  5535  func TestNoDebug_AcceptNoRequestsButDisconnect(t *testing.T) {
  5536  	runTest(t, "http_server", func(client *daptest.Client, fixture protest.Fixture) {
  5537  		client.InitializeRequest()
  5538  		client.ExpectInitializeResponseAndCapabilities(t)
  5539  		client.LaunchRequestWithArgs(map[string]interface{}{
  5540  			"noDebug": true, "mode": "exec", "program": fixture.Path,
  5541  		})
  5542  		client.ExpectLaunchResponse(t)
  5543  
  5544  		// Anything other than disconnect should get rejected
  5545  		ExpectNoDebugError := func(cmd string) {
  5546  			er := client.ExpectErrorResponse(t)
  5547  			if !checkErrorMessageFormat(er.Body.Error, fmt.Sprintf("noDebug mode: unable to process '%s' request", cmd)) {
  5548  				t.Errorf("\ngot %#v\nwant 'noDebug mode: unable to process '%s' request'", er, cmd)
  5549  			}
  5550  		}
  5551  		client.SetBreakpointsRequest(fixture.Source, []int{8})
  5552  		ExpectNoDebugError("setBreakpoints")
  5553  		client.SetFunctionBreakpointsRequest(nil)
  5554  		ExpectNoDebugError("setFunctionBreakpoints")
  5555  		client.PauseRequest(1)
  5556  		ExpectNoDebugError("pause")
  5557  		client.RestartRequest()
  5558  		client.ExpectUnsupportedCommandErrorResponse(t)
  5559  
  5560  		// Disconnect request is ok
  5561  		client.DisconnectRequestWithKillOption(true)
  5562  		terminated, disconnectResp := false, false
  5563  		for {
  5564  			m, err := client.ReadMessage()
  5565  			if err != nil {
  5566  				break
  5567  			}
  5568  			switch m := m.(type) {
  5569  			case *dap.OutputEvent:
  5570  				ok := false
  5571  				wants := []string{`Terminating process [0-9]+\n`, fmt.Sprintf(daptest.ProcessExited, "(-1|1)")}
  5572  				for _, want := range wants {
  5573  					if matched, _ := regexp.MatchString(want, m.Body.Output); matched {
  5574  						ok = true
  5575  						break
  5576  					}
  5577  				}
  5578  				if !ok {
  5579  					t.Errorf("\ngot %#v\nwant Output=%q\n", m, wants)
  5580  				}
  5581  			case *dap.TerminatedEvent:
  5582  				terminated = true
  5583  			case *dap.DisconnectResponse:
  5584  				disconnectResp = true
  5585  			default:
  5586  				t.Errorf("got unexpected message %#v", m)
  5587  			}
  5588  		}
  5589  		if !terminated {
  5590  			t.Errorf("did not get TerminatedEvent")
  5591  		}
  5592  		if !disconnectResp {
  5593  			t.Errorf("did not get DisconnectResponse")
  5594  		}
  5595  	})
  5596  }
  5597  
  5598  func TestLaunchRequestWithRelativeBuildPath(t *testing.T) {
  5599  	serverStopped := make(chan struct{})
  5600  	client := startDAPServerWithClient(t, false, serverStopped)
  5601  	defer client.Close()
  5602  
  5603  	fixdir := protest.FindFixturesDir()
  5604  	if filepath.IsAbs(fixdir) {
  5605  		t.Fatal("this test requires relative program path")
  5606  	}
  5607  	program := filepath.Join(protest.FindFixturesDir(), "buildtest")
  5608  
  5609  	// Use different working dir for target than dlv.
  5610  	// Program path will be interpreted relative to dlv's.
  5611  	dlvwd, _ := os.Getwd()
  5612  	runDebugSession(t, client, "launch", func() {
  5613  		client.LaunchRequestWithArgs(map[string]interface{}{
  5614  			"mode": "debug", "program": program, "cwd": filepath.Dir(dlvwd),
  5615  		})
  5616  	})
  5617  	<-serverStopped
  5618  }
  5619  
  5620  func TestLaunchRequestWithRelativeExecPath(t *testing.T) {
  5621  	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
  5622  		symlink := "./__thisexe"
  5623  		err := os.Symlink(fixture.Path, symlink)
  5624  		if err != nil {
  5625  			if runtime.GOOS == "windows" {
  5626  				t.Skip("this test requires symlinks to be enabled and allowed")
  5627  			} else {
  5628  				t.Fatal("unable to create relative symlink:", err)
  5629  			}
  5630  		}
  5631  		defer os.Remove(symlink)
  5632  		runDebugSession(t, client, "launch", func() {
  5633  			client.LaunchRequestWithArgs(map[string]interface{}{
  5634  				"mode": "exec", "program": symlink,
  5635  			})
  5636  		})
  5637  	})
  5638  }
  5639  
  5640  func TestLaunchTestRequest(t *testing.T) {
  5641  	orgWD, _ := os.Getwd()
  5642  	fixtures := protest.FindFixturesDir() // relative to current working directory.
  5643  	absoluteFixturesDir, _ := filepath.Abs(fixtures)
  5644  	absolutePkgDir, _ := filepath.Abs(filepath.Join(fixtures, "buildtest"))
  5645  	testFile := filepath.Join(absolutePkgDir, "main_test.go")
  5646  
  5647  	for _, tc := range []struct {
  5648  		name       string
  5649  		dlvWD      string
  5650  		launchArgs map[string]interface{}
  5651  		wantWD     string
  5652  	}{{
  5653  		name: "default",
  5654  		launchArgs: map[string]interface{}{
  5655  			"mode": "test", "program": absolutePkgDir,
  5656  		},
  5657  		wantWD: absolutePkgDir,
  5658  	}, {
  5659  		name: "output",
  5660  		launchArgs: map[string]interface{}{
  5661  			"mode": "test", "program": absolutePkgDir, "output": "test.out",
  5662  		},
  5663  		wantWD: absolutePkgDir,
  5664  	}, {
  5665  		name: "dlvCwd",
  5666  		launchArgs: map[string]interface{}{
  5667  			"mode": "test", "program": absolutePkgDir, "dlvCwd": ".",
  5668  		},
  5669  		wantWD: absolutePkgDir,
  5670  	}, {
  5671  		name: "dlvCwd2",
  5672  		launchArgs: map[string]interface{}{
  5673  			"mode": "test", "program": ".", "dlvCwd": absolutePkgDir,
  5674  		},
  5675  		wantWD: absolutePkgDir,
  5676  	}, {
  5677  		name: "cwd",
  5678  		launchArgs: map[string]interface{}{
  5679  			"mode": "test", "program": absolutePkgDir, "cwd": fixtures, // fixtures is relative to the current working directory.
  5680  		},
  5681  		wantWD: absoluteFixturesDir,
  5682  	}, {
  5683  		name:  "dlv runs outside of module",
  5684  		dlvWD: os.TempDir(),
  5685  		launchArgs: map[string]interface{}{
  5686  			"mode": "test", "program": absolutePkgDir, "dlvCwd": absoluteFixturesDir,
  5687  		},
  5688  		wantWD: absolutePkgDir,
  5689  	}, {
  5690  		name:  "dlv builds in dlvCwd but runs in cwd",
  5691  		dlvWD: fixtures,
  5692  		launchArgs: map[string]interface{}{
  5693  			"mode": "test", "program": absolutePkgDir, "dlvCwd": absolutePkgDir, "cwd": "..", // relative to dlvCwd.
  5694  		},
  5695  		wantWD: absoluteFixturesDir,
  5696  	}} {
  5697  		t.Run(tc.name, func(t *testing.T) {
  5698  			// Some test cases with dlvCwd or dlvWD change process working directory.
  5699  			defer os.Chdir(orgWD)
  5700  			if tc.dlvWD != "" {
  5701  				os.Chdir(tc.dlvWD)
  5702  				defer os.Chdir(orgWD)
  5703  			}
  5704  			serverStopped := make(chan struct{})
  5705  			client := startDAPServerWithClient(t, false, serverStopped)
  5706  			defer client.Close()
  5707  
  5708  			runDebugSessionWithBPs(t, client, "launch",
  5709  				func() { // Launch
  5710  					client.LaunchRequestWithArgs(tc.launchArgs)
  5711  				},
  5712  				testFile, []int{14},
  5713  				[]onBreakpoint{{
  5714  					execute: func() {
  5715  						checkStop(t, client, -1, "buildtest.TestCurrentDirectory", 14)
  5716  						client.VariablesRequest(1001) // Locals
  5717  						locals := client.ExpectVariablesResponse(t)
  5718  						checkChildren(t, locals, "Locals", 1)
  5719  						for i := range locals.Body.Variables {
  5720  							switch locals.Body.Variables[i].Name {
  5721  							case "wd": // The test's working directory is the package directory by default.
  5722  								checkVarExact(t, locals, i, "wd", "wd", fmt.Sprintf("%q", tc.wantWD), "string", noChildren)
  5723  							}
  5724  						}
  5725  					},
  5726  				}})
  5727  
  5728  			<-serverStopped
  5729  		})
  5730  	}
  5731  }
  5732  
  5733  // Tests that 'args' from LaunchRequest are parsed and passed to the target
  5734  // program. The target program exits without an error on success, and
  5735  // panics on error, causing an unexpected StoppedEvent instead of
  5736  // Terminated Event.
  5737  func TestLaunchRequestWithArgs(t *testing.T) {
  5738  	runTest(t, "testargs", func(client *daptest.Client, fixture protest.Fixture) {
  5739  		runDebugSession(t, client, "launch", func() {
  5740  			client.LaunchRequestWithArgs(map[string]interface{}{
  5741  				"mode": "exec", "program": fixture.Path,
  5742  				"args": []string{"test", "pass flag"},
  5743  			})
  5744  		})
  5745  	})
  5746  }
  5747  
  5748  // Tests that 'buildFlags' from LaunchRequest are parsed and passed to the
  5749  // compiler. The target program exits without an error on success, and
  5750  // panics on error, causing an unexpected StoppedEvent instead of
  5751  // TerminatedEvent.
  5752  func TestLaunchRequestWithBuildFlags(t *testing.T) {
  5753  	runTest(t, "buildflagtest", func(client *daptest.Client, fixture protest.Fixture) {
  5754  		runDebugSession(t, client, "launch", func() {
  5755  			// We reuse the harness that builds, but ignore the built binary,
  5756  			// only relying on the source to be built in response to LaunchRequest.
  5757  			client.LaunchRequestWithArgs(map[string]interface{}{
  5758  				"mode": "debug", "program": fixture.Source, "output": "__mybin",
  5759  				"buildFlags": "-ldflags '-X main.Hello=World'",
  5760  			})
  5761  		})
  5762  	})
  5763  }
  5764  
  5765  func TestLaunchRequestWithBuildFlags2(t *testing.T) {
  5766  	runTest(t, "buildflagtest", func(client *daptest.Client, fixture protest.Fixture) {
  5767  		runDebugSession(t, client, "launch", func() {
  5768  			// We reuse the harness that builds, but ignore the built binary,
  5769  			// only relying on the source to be built in response to LaunchRequest.
  5770  			client.LaunchRequestWithArgs(map[string]interface{}{
  5771  				"mode": "debug", "program": fixture.Source, "output": "__mybin",
  5772  				"buildFlags": []string{"-ldflags", "-X main.Hello=World"},
  5773  			})
  5774  		})
  5775  	})
  5776  }
  5777  
  5778  func TestLaunchRequestWithEnv(t *testing.T) {
  5779  	// testenv fixture will lookup SOMEVAR with
  5780  	//   x, y := os.Lookup("SOMEVAR")
  5781  	// before stopping at runtime.Breakpoint.
  5782  
  5783  	type envMap map[string]*string
  5784  	strVar := func(s string) *string { return &s }
  5785  
  5786  	fixtures := protest.FindFixturesDir() // relative to current working directory.
  5787  	testFile, _ := filepath.Abs(filepath.Join(fixtures, "testenv.go"))
  5788  	for _, tc := range []struct {
  5789  		name      string
  5790  		initEnv   envMap
  5791  		launchEnv envMap
  5792  		wantX     string
  5793  		wantY     bool
  5794  	}{
  5795  		{
  5796  			name:    "no env",
  5797  			initEnv: envMap{"SOMEVAR": strVar("baz")},
  5798  			wantX:   "baz",
  5799  			wantY:   true,
  5800  		},
  5801  		{
  5802  			name:      "overwrite",
  5803  			initEnv:   envMap{"SOMEVAR": strVar("baz")},
  5804  			launchEnv: envMap{"SOMEVAR": strVar("bar")},
  5805  			wantX:     "bar",
  5806  			wantY:     true,
  5807  		},
  5808  		{
  5809  			name:      "unset",
  5810  			initEnv:   envMap{"SOMEVAR": strVar("baz")},
  5811  			launchEnv: envMap{"SOMEVAR": nil},
  5812  			wantX:     "",
  5813  			wantY:     false,
  5814  		},
  5815  		{
  5816  			name:      "empty value",
  5817  			initEnv:   envMap{"SOMEVAR": strVar("baz")},
  5818  			launchEnv: envMap{"SOMEVAR": strVar("")},
  5819  			wantX:     "",
  5820  			wantY:     true,
  5821  		},
  5822  		{
  5823  			name:      "set",
  5824  			launchEnv: envMap{"SOMEVAR": strVar("foo")},
  5825  			wantX:     "foo",
  5826  			wantY:     true,
  5827  		},
  5828  		{
  5829  			name:      "untouched",
  5830  			initEnv:   envMap{"SOMEVAR": strVar("baz")},
  5831  			launchEnv: envMap{"SOMEVAR2": nil, "SOMEVAR3": strVar("foo")},
  5832  			wantX:     "baz",
  5833  			wantY:     true,
  5834  		},
  5835  	} {
  5836  		t.Run(tc.name, func(t *testing.T) {
  5837  			for k, v := range tc.initEnv {
  5838  				if v != nil {
  5839  					t.Setenv(k, *v)
  5840  				}
  5841  			}
  5842  
  5843  			serverStopped := make(chan struct{})
  5844  			client := startDAPServerWithClient(t, false, serverStopped)
  5845  			defer client.Close()
  5846  
  5847  			runDebugSessionWithBPs(t, client, "launch", func() { // launch
  5848  				client.LaunchRequestWithArgs(map[string]interface{}{
  5849  					"mode":    "debug",
  5850  					"program": testFile,
  5851  					"env":     tc.launchEnv,
  5852  				})
  5853  			}, testFile, nil, // runtime.Breakpoint
  5854  				[]onBreakpoint{{
  5855  					execute: func() {
  5856  						client.EvaluateRequest("x", 1000, "whatever")
  5857  						gotX := client.ExpectEvaluateResponse(t)
  5858  						checkEval(t, gotX, fmt.Sprintf("%q", tc.wantX), false)
  5859  						client.EvaluateRequest("y", 1000, "whatever")
  5860  						gotY := client.ExpectEvaluateResponse(t)
  5861  						checkEval(t, gotY, fmt.Sprintf("%v", tc.wantY), false)
  5862  					},
  5863  					disconnect: true,
  5864  				}})
  5865  			<-serverStopped
  5866  		})
  5867  	}
  5868  }
  5869  
  5870  func TestAttachRequest(t *testing.T) {
  5871  	if runtime.GOOS == "windows" {
  5872  		t.Skip("test skipped on Windows, see https://delve.teamcity.com/project/Delve_windows for details")
  5873  	}
  5874  	runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
  5875  		// Start the program to attach to
  5876  		cmd := execFixture(t, fixture)
  5877  
  5878  		runDebugSessionWithBPs(t, client, "attach",
  5879  			// Attach
  5880  			func() {
  5881  				client.AttachRequest(map[string]interface{}{
  5882  					/*"mode": "local" by default*/ "processId": cmd.Process.Pid, "stopOnEntry": false,
  5883  				})
  5884  				client.ExpectCapabilitiesEventSupportTerminateDebuggee(t)
  5885  			},
  5886  			// Set breakpoints
  5887  			fixture.Source, []int{8},
  5888  			[]onBreakpoint{{
  5889  				// Stop at line 8
  5890  				execute: func() {
  5891  					checkStop(t, client, 1, "main.loop", 8)
  5892  					client.VariablesRequest(localsScope)
  5893  					locals := client.ExpectVariablesResponse(t)
  5894  					checkChildren(t, locals, "Locals", 1)
  5895  					checkVarRegex(t, locals, 0, "i", "i", "[0-9]+", "int", noChildren)
  5896  				},
  5897  				disconnect: true,
  5898  			}})
  5899  	})
  5900  }
  5901  
  5902  func TestAttachWaitForRequest(t *testing.T) {
  5903  	if runtime.GOOS == "freebsd" {
  5904  		// The value of /proc/[pid]/cmdline might be wrong on FreeBSD: zero or the same as the parent process.
  5905  		t.Skip("test skipped on freebsd")
  5906  	}
  5907  	runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
  5908  		cmd := exec.Command(fixture.Path)
  5909  		stdout, err := cmd.StdoutPipe()
  5910  		if err != nil {
  5911  			t.Fatal(err)
  5912  		}
  5913  		cmd.Stderr = os.Stderr
  5914  		if err := cmd.Start(); err != nil {
  5915  			t.Fatal(err)
  5916  		}
  5917  		// Wait for output.
  5918  		// This will give the target process time to initialize the runtime before we attach,
  5919  		// so we can rely on having goroutines when they are requested on attach.
  5920  		scanOut := bufio.NewScanner(stdout)
  5921  		scanOut.Scan()
  5922  		if scanOut.Text() != "past main" {
  5923  			t.Errorf("expected loopprog.go to output \"past main\"")
  5924  		}
  5925  
  5926  		t.Logf("The process id of program %q is %d", cmd.Path, cmd.Process.Pid)
  5927  
  5928  		client.InitializeRequest()
  5929  		client.ExpectInitializeResponseAndCapabilities(t)
  5930  		client.AttachRequest(map[string]interface{}{
  5931  			"mode":        "local",
  5932  			"waitFor":     fixture.Path,
  5933  			"stopOnEntry": true,
  5934  		})
  5935  		client.ExpectCapabilitiesEventSupportTerminateDebuggee(t)
  5936  		client.ExpectInitializedEvent(t)
  5937  		client.ExpectAttachResponse(t)
  5938  		client.DisconnectRequestWithKillOption(true)
  5939  		client.ExpectOutputEvent(t)
  5940  		client.ExpectDisconnectResponse(t)
  5941  		client.ExpectTerminatedEvent(t)
  5942  
  5943  		runtime.KeepAlive(stdout)
  5944  	})
  5945  }
  5946  
  5947  // Since we are in async mode while running, we might receive thee messages after pause request
  5948  // in either order.
  5949  func expectPauseResponseAndStoppedEvent(t *testing.T, client *daptest.Client) {
  5950  	t.Helper()
  5951  	for i := 0; i < 2; i++ {
  5952  		msg := client.ExpectMessage(t)
  5953  		switch m := msg.(type) {
  5954  		case *dap.StoppedEvent:
  5955  			if m.Body.Reason != "pause" || m.Body.ThreadId != 0 && m.Body.ThreadId != 1 {
  5956  				t.Errorf("\ngot %#v\nwant ThreadId=0/1 Reason='pause'", m)
  5957  			}
  5958  		case *dap.PauseResponse:
  5959  		default:
  5960  			t.Fatalf("got %#v, want StoppedEvent or PauseResponse", m)
  5961  		}
  5962  	}
  5963  }
  5964  
  5965  func TestPauseAndContinue(t *testing.T) {
  5966  	runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
  5967  		runDebugSessionWithBPs(t, client, "launch",
  5968  			// Launch
  5969  			func() {
  5970  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  5971  			},
  5972  			// Set breakpoints
  5973  			fixture.Source, []int{6},
  5974  			[]onBreakpoint{{
  5975  				execute: func() {
  5976  					client.CheckStopLocation(t, 1, "main.loop", 6)
  5977  
  5978  					// Continue resumes all goroutines, so thread id is ignored
  5979  					client.ContinueRequest(12345)
  5980  					client.ExpectContinueResponse(t)
  5981  
  5982  					time.Sleep(time.Second)
  5983  
  5984  					// Halt pauses all goroutines, so thread id is ignored
  5985  					client.PauseRequest(56789)
  5986  					expectPauseResponseAndStoppedEvent(t, client)
  5987  
  5988  					// Pause will be a no-op at a pause: there will be no additional stopped events
  5989  					client.PauseRequest(1)
  5990  					client.ExpectPauseResponse(t)
  5991  				},
  5992  				// The program has an infinite loop, so we must kill it by disconnecting.
  5993  				disconnect: true,
  5994  			}})
  5995  	})
  5996  }
  5997  
  5998  func TestUnsupportedCommandResponses(t *testing.T) {
  5999  	var got *dap.ErrorResponse
  6000  	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
  6001  		seqCnt := 1
  6002  		expectUnsupportedCommand := func(cmd string) {
  6003  			t.Helper()
  6004  			got = client.ExpectUnsupportedCommandErrorResponse(t)
  6005  			if got.RequestSeq != seqCnt || got.Command != cmd {
  6006  				t.Errorf("\ngot  %#v\nwant RequestSeq=%d Command=%s", got, seqCnt, cmd)
  6007  			}
  6008  			seqCnt++
  6009  		}
  6010  
  6011  		client.RestartFrameRequest()
  6012  		expectUnsupportedCommand("restartFrame")
  6013  
  6014  		client.GotoRequest()
  6015  		expectUnsupportedCommand("goto")
  6016  
  6017  		client.SourceRequest()
  6018  		expectUnsupportedCommand("source")
  6019  
  6020  		client.TerminateThreadsRequest()
  6021  		expectUnsupportedCommand("terminateThreads")
  6022  
  6023  		client.StepInTargetsRequest()
  6024  		expectUnsupportedCommand("stepInTargets")
  6025  
  6026  		client.GotoTargetsRequest()
  6027  		expectUnsupportedCommand("gotoTargets")
  6028  
  6029  		client.CompletionsRequest()
  6030  		expectUnsupportedCommand("completions")
  6031  
  6032  		client.DataBreakpointInfoRequest()
  6033  		expectUnsupportedCommand("dataBreakpointInfo")
  6034  
  6035  		client.SetDataBreakpointsRequest()
  6036  		expectUnsupportedCommand("setDataBreakpoints")
  6037  
  6038  		client.BreakpointLocationsRequest()
  6039  		expectUnsupportedCommand("breakpointLocations")
  6040  
  6041  		client.ModulesRequest()
  6042  		expectUnsupportedCommand("modules")
  6043  
  6044  		client.DisconnectRequest()
  6045  		client.ExpectDisconnectResponse(t)
  6046  	})
  6047  }
  6048  
  6049  type helperForSetVariable struct {
  6050  	t *testing.T
  6051  	c *daptest.Client
  6052  }
  6053  
  6054  func (h *helperForSetVariable) expectSetVariable(ref int, name, value string) {
  6055  	h.t.Helper()
  6056  	h.expectSetVariable0(ref, name, value, false)
  6057  }
  6058  
  6059  func (h *helperForSetVariable) failSetVariable(ref int, name, value, wantErrInfo string) {
  6060  	h.t.Helper()
  6061  	h.failSetVariable0(ref, name, value, wantErrInfo, false)
  6062  }
  6063  
  6064  func (h *helperForSetVariable) failSetVariableAndStop(ref int, name, value, wantErrInfo string) {
  6065  	h.t.Helper()
  6066  	h.failSetVariable0(ref, name, value, wantErrInfo, true)
  6067  }
  6068  
  6069  func (h *helperForSetVariable) evaluate(expr, want string, hasRef bool) {
  6070  	h.t.Helper()
  6071  	h.c.EvaluateRequest(expr, 1000, "whatever")
  6072  	got := h.c.ExpectEvaluateResponse(h.t)
  6073  	checkEval(h.t, got, want, hasRef)
  6074  }
  6075  
  6076  func (h *helperForSetVariable) evaluateRegex(expr, want string, hasRef bool) {
  6077  	h.t.Helper()
  6078  	h.c.EvaluateRequest(expr, 1000, "whatever")
  6079  	got := h.c.ExpectEvaluateResponse(h.t)
  6080  	checkEvalRegex(h.t, got, want, hasRef)
  6081  }
  6082  
  6083  func (h *helperForSetVariable) expectSetVariable0(ref int, name, value string, wantStop bool) {
  6084  	h.t.Helper()
  6085  
  6086  	h.c.SetVariableRequest(ref, name, value)
  6087  	if wantStop {
  6088  		h.c.ExpectStoppedEvent(h.t)
  6089  	}
  6090  	if got, want := h.c.ExpectSetVariableResponse(h.t), value; got.Success != true || got.Body.Value != want {
  6091  		h.t.Errorf("SetVariableRequest(%v, %v)=%#v, want {Success=true, Body.Value=%q", name, value, got, want)
  6092  	}
  6093  }
  6094  
  6095  func (h *helperForSetVariable) failSetVariable0(ref int, name, value, wantErrInfo string, wantStop bool) {
  6096  	h.t.Helper()
  6097  
  6098  	h.c.SetVariableRequest(ref, name, value)
  6099  	if wantStop {
  6100  		h.c.ExpectStoppedEvent(h.t)
  6101  	}
  6102  	resp := h.c.ExpectErrorResponse(h.t)
  6103  	if got := resp.Body.Error; !stringContainsCaseInsensitive(got.Format, wantErrInfo) {
  6104  		h.t.Errorf("got %#v, want error string containing %v", got, wantErrInfo)
  6105  	}
  6106  }
  6107  
  6108  func (h *helperForSetVariable) variables(ref int) *dap.VariablesResponse {
  6109  	h.t.Helper()
  6110  	h.c.VariablesRequest(ref)
  6111  	return h.c.ExpectVariablesResponse(h.t)
  6112  }
  6113  
  6114  // TestSetVariable tests SetVariable features that do not need function call support.
  6115  func TestSetVariable(t *testing.T) {
  6116  	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {
  6117  		runDebugSessionWithBPs(t, client, "launch",
  6118  			func() {
  6119  				client.LaunchRequestWithArgs(map[string]interface{}{
  6120  					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,
  6121  				})
  6122  			},
  6123  			fixture.Source, []int{}, // breakpoints are set within the program.
  6124  			[]onBreakpoint{{
  6125  				execute: func() {
  6126  					tester := &helperForSetVariable{t, client}
  6127  
  6128  					checkStop(t, client, 1, "main.foobar", []int{65, 66})
  6129  
  6130  					// Local variables
  6131  					locals := tester.variables(localsScope)
  6132  
  6133  					// Args of foobar(baz string, bar FooBar)
  6134  					checkVarExact(t, locals, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, "main.FooBar", hasChildren)
  6135  					tester.failSetVariable(localsScope, "bar", `main.FooBar {Baz: 42, Bur: "ipsum"}`, "*ast.CompositeLit not implemented")
  6136  
  6137  					// Nested field.
  6138  					barRef := checkVarExact(t, locals, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, "main.FooBar", hasChildren)
  6139  					tester.expectSetVariable(barRef, "Baz", "42")
  6140  					tester.evaluate("bar", `main.FooBar {Baz: 42, Bur: "lorem"}`, hasChildren)
  6141  
  6142  					tester.failSetVariable(barRef, "Baz", `"string"`, "can not convert")
  6143  
  6144  					// int
  6145  					checkVarExact(t, locals, -1, "a2", "a2", "6", "int", noChildren)
  6146  					tester.expectSetVariable(localsScope, "a2", "42")
  6147  					tester.evaluate("a2", "42", noChildren)
  6148  
  6149  					tester.failSetVariable(localsScope, "a2", "false", "can not convert")
  6150  
  6151  					// float
  6152  					checkVarExact(t, locals, -1, "a3", "a3", "7.23", "float64", noChildren)
  6153  					tester.expectSetVariable(localsScope, "a3", "-0.1")
  6154  					tester.evaluate("a3", "-0.1", noChildren)
  6155  
  6156  					// array of int
  6157  					a4Ref := checkVarExact(t, locals, -1, "a4", "a4", "[2]int [1,2]", "[2]int", hasChildren)
  6158  					tester.expectSetVariable(a4Ref, "[1]", "-7")
  6159  					tester.evaluate("a4", "[2]int [1,-7]", hasChildren)
  6160  
  6161  					tester.failSetVariable(localsScope, "a4", "[2]int{3, 4}", "not implemented")
  6162  
  6163  					// slice of int
  6164  					a5Ref := checkVarExact(t, locals, -1, "a5", "a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "[]int", hasChildren)
  6165  					tester.expectSetVariable(a5Ref, "[3]", "100")
  6166  					tester.evaluate("a5", "[]int len: 5, cap: 5, [1,2,3,100,5]", hasChildren)
  6167  
  6168  					// composite literal and its nested fields.
  6169  					a7Ref := checkVarExact(t, locals, -1, "a7", "a7", `*main.FooBar {Baz: 5, Bur: "strum"}`, "*main.FooBar", hasChildren)
  6170  					a7Val := tester.variables(a7Ref)
  6171  					a7ValRef := checkVarExact(t, a7Val, -1, "", "(*a7)", `main.FooBar {Baz: 5, Bur: "strum"}`, "main.FooBar", hasChildren)
  6172  					tester.expectSetVariable(a7ValRef, "Baz", "7")
  6173  					tester.evaluate("(*a7)", `main.FooBar {Baz: 7, Bur: "strum"}`, hasChildren)
  6174  
  6175  					// pointer
  6176  					checkVarExact(t, locals, -1, "a9", "a9", `*main.FooBar nil`, "*main.FooBar", noChildren)
  6177  					tester.expectSetVariable(localsScope, "a9", "&a6")
  6178  					tester.evaluate("a9", `*main.FooBar {Baz: 8, Bur: "word"}`, hasChildren)
  6179  
  6180  					// slice of pointers
  6181  					a13Ref := checkVarExact(t, locals, -1, "a13", "a13", `[]*main.FooBar len: 3, cap: 3, [*{Baz: 6, Bur: "f"},*{Baz: 7, Bur: "g"},*{Baz: 8, Bur: "h"}]`, "[]*main.FooBar", hasChildren)
  6182  					a13 := tester.variables(a13Ref)
  6183  					a13c0Ref := checkVarExact(t, a13, -1, "[0]", "a13[0]", `*main.FooBar {Baz: 6, Bur: "f"}`, "*main.FooBar", hasChildren)
  6184  					a13c0 := tester.variables(a13c0Ref)
  6185  					a13c0valRef := checkVarExact(t, a13c0, -1, "", "(*a13[0])", `main.FooBar {Baz: 6, Bur: "f"}`, "main.FooBar", hasChildren)
  6186  					tester.expectSetVariable(a13c0valRef, "Baz", "777")
  6187  					tester.evaluate("a13[0]", `*main.FooBar {Baz: 777, Bur: "f"}`, hasChildren)
  6188  
  6189  					// complex
  6190  					tester.evaluate("c64", `(1 + 2i)`, hasChildren)
  6191  					tester.expectSetVariable(localsScope, "c64", "(2 + 3i)")
  6192  					tester.evaluate("c64", `(2 + 3i)`, hasChildren)
  6193  					// note: complex's real, imaginary part can't be directly mutable.
  6194  
  6195  					//
  6196  					// Global variables
  6197  					//    p1 = 10
  6198  					client.VariablesRequest(globalsScope)
  6199  					globals := client.ExpectVariablesResponse(t)
  6200  
  6201  					checkVarExact(t, globals, -1, "p1", "main.p1", "10", "int", noChildren)
  6202  					tester.expectSetVariable(globalsScope, "p1", "-10")
  6203  					tester.evaluate("p1", "-10", noChildren)
  6204  					tester.failSetVariable(globalsScope, "p1", "0.1", "can not convert")
  6205  				},
  6206  				disconnect: true,
  6207  			}})
  6208  	})
  6209  
  6210  	runTest(t, "testvariables2", func(client *daptest.Client, fixture protest.Fixture) {
  6211  		runDebugSessionWithBPs(t, client, "launch",
  6212  			func() {
  6213  				client.LaunchRequestWithArgs(map[string]interface{}{
  6214  					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,
  6215  				})
  6216  			},
  6217  			fixture.Source, []int{}, // breakpoints are set within the program.
  6218  			[]onBreakpoint{{
  6219  				execute: func() {
  6220  					tester := &helperForSetVariable{t, client}
  6221  
  6222  					checkStop(t, client, 1, "main.main", -1)
  6223  					locals := tester.variables(localsScope)
  6224  
  6225  					// channel
  6226  					tester.evaluate("chnil", "chan int nil", noChildren)
  6227  					tester.expectSetVariable(localsScope, "chnil", "ch1")
  6228  					tester.evaluate("chnil", "chan int 4/11", hasChildren)
  6229  
  6230  					// func
  6231  					tester.evaluate("fn2", "nil", noChildren)
  6232  					tester.expectSetVariable(localsScope, "fn2", "fn1")
  6233  					tester.evaluate("fn2", "main.afunc", noChildren)
  6234  
  6235  					// interface
  6236  					tester.evaluate("ifacenil", "interface {} nil", noChildren)
  6237  					tester.expectSetVariable(localsScope, "ifacenil", "iface1")
  6238  					tester.evaluate("ifacenil", "interface {}(*main.astruct) *{A: 1, B: 2}", hasChildren)
  6239  
  6240  					// interface.(data)
  6241  					iface1Ref := checkVarExact(t, locals, -1, "iface1", "iface1", "interface {}(*main.astruct) *{A: 1, B: 2}", "interface {}(*main.astruct)", hasChildren)
  6242  					iface1 := tester.variables(iface1Ref)
  6243  					iface1DataRef := checkVarExact(t, iface1, -1, "data", "iface1.(data)", "*main.astruct {A: 1, B: 2}", "*main.astruct", hasChildren)
  6244  					iface1Data := tester.variables(iface1DataRef)
  6245  					iface1DataValueRef := checkVarExact(t, iface1Data, -1, "", "(*iface1.(data))", "main.astruct {A: 1, B: 2}", "main.astruct", hasChildren)
  6246  					tester.expectSetVariable(iface1DataValueRef, "A", "2021")
  6247  					tester.evaluate("iface1", "interface {}(*main.astruct) *{A: 2021, B: 2}", hasChildren)
  6248  
  6249  					// map: string -> struct
  6250  					tester.evaluate(`m1["Malone"]`, "main.astruct {A: 2, B: 3}", hasChildren)
  6251  					m1Ref := checkVarRegex(t, locals, -1, "m1", "m1", `.*map\[string\]main\.astruct.*`, `map\[string\]main\.astruct`, hasChildren)
  6252  					m1 := tester.variables(m1Ref)
  6253  					elem1 := m1.Body.Variables[1]
  6254  					tester.expectSetVariable(elem1.VariablesReference, "A", "-9999")
  6255  					tester.expectSetVariable(elem1.VariablesReference, "B", "10000")
  6256  					tester.evaluate(elem1.EvaluateName, "main.astruct {A: -9999, B: 10000}", hasChildren)
  6257  
  6258  					// map: struct -> int
  6259  					m3Ref := checkVarExact(t, locals, -1, "m3", "m3", "map[main.astruct]int [{A: 1, B: 1}: 42, {A: 2, B: 2}: 43, ]", "map[main.astruct]int", hasChildren)
  6260  					tester.expectSetVariable(m3Ref, "main.astruct {A: 1, B: 1}", "8888")
  6261  					// note: updating keys is possible, but let's not promise anything.
  6262  					tester.evaluateRegex("m3", `.*\[\{A: 1, B: 1\}: 8888,.*`, hasChildren)
  6263  
  6264  					// map: struct -> struct
  6265  					m4Ref := checkVarRegex(t, locals, -1, "m4", "m4", `map\[main\.astruct]main\.astruct.*\[\{A: 1, B: 1\}: \{A: 11, B: 11\}.*`, `map\[main\.astruct\]main\.astruct`, hasChildren)
  6266  					m4 := tester.variables(m4Ref)
  6267  					m4Val1Ref := checkVarRegex(t, m4, -1, "[val 0]", `.*0x[0-9a-f]+.*`, `main.astruct.*`, `main\.astruct`, hasChildren)
  6268  					tester.expectSetVariable(m4Val1Ref, "A", "-9999")
  6269  					tester.evaluateRegex("m4", `.*A: -9999,.*`, hasChildren)
  6270  
  6271  					// unsigned pointer
  6272  					checkVarRegex(t, locals, -1, "up1", "up1", `unsafe\.Pointer\(0x[0-9a-f]+\)`, "unsafe.Pointer", noChildren)
  6273  					tester.expectSetVariable(localsScope, "up1", "unsafe.Pointer(0x0)")
  6274  					tester.evaluate("up1", "unsafe.Pointer(0x0)", noChildren)
  6275  
  6276  					// val := A{val: 1}
  6277  					valRef := checkVarExact(t, locals, -1, "val", "val", `main.A {val: 1}`, "main.A", hasChildren)
  6278  					tester.expectSetVariable(valRef, "val", "3")
  6279  					tester.evaluate("val", `main.A {val: 3}`, hasChildren)
  6280  				},
  6281  				disconnect: true,
  6282  			}})
  6283  	})
  6284  }
  6285  
  6286  // TestSetVariableWithCall tests SetVariable features that do not depend on function calls support.
  6287  func TestSetVariableWithCall(t *testing.T) {
  6288  	protest.MustSupportFunctionCalls(t, testBackend)
  6289  
  6290  	runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) {
  6291  		runDebugSessionWithBPs(t, client, "launch",
  6292  			func() {
  6293  				client.LaunchRequestWithArgs(map[string]interface{}{
  6294  					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,
  6295  				})
  6296  			},
  6297  			fixture.Source, []int{},
  6298  			[]onBreakpoint{{
  6299  				execute: func() {
  6300  					tester := &helperForSetVariable{t, client}
  6301  
  6302  					checkStop(t, client, 1, "main.foobar", []int{65, 66})
  6303  
  6304  					// Local variables
  6305  					locals := tester.variables(localsScope)
  6306  
  6307  					// Args of foobar(baz string, bar FooBar)
  6308  					checkVarExact(t, locals, 0, "baz", "baz", `"bazburzum"`, "string", noChildren)
  6309  					tester.expectSetVariable(localsScope, "baz", `"BazBurZum"`)
  6310  					tester.evaluate("baz", `"BazBurZum"`, noChildren)
  6311  
  6312  					barRef := checkVarExact(t, locals, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, "main.FooBar", hasChildren)
  6313  					tester.expectSetVariable(barRef, "Bur", `"ipsum"`)
  6314  					tester.evaluate("bar", `main.FooBar {Baz: 10, Bur: "ipsum"}`, hasChildren)
  6315  
  6316  					checkVarExact(t, locals, -1, "a1", "a1", `"foofoofoofoofoofoo"`, "string", noChildren)
  6317  					tester.expectSetVariable(localsScope, "a1", `"barbarbar"`)
  6318  					tester.evaluate("a1", `"barbarbar"`, noChildren)
  6319  
  6320  					a6Ref := checkVarExact(t, locals, -1, "a6", "a6", `main.FooBar {Baz: 8, Bur: "word"}`, "main.FooBar", hasChildren)
  6321  					tester.failSetVariable(a6Ref, "Bur", "false", "can not convert")
  6322  
  6323  					tester.expectSetVariable(a6Ref, "Bur", `"sentence"`)
  6324  					tester.evaluate("a6", `main.FooBar {Baz: 8, Bur: "sentence"}`, hasChildren)
  6325  
  6326  					// stop inside main.barfoo
  6327  					client.ContinueRequest(1)
  6328  					client.ExpectContinueResponse(t)
  6329  					client.ExpectStoppedEvent(t)
  6330  					checkStop(t, client, 1, "main.barfoo", -1)
  6331  
  6332  					// Test: set string 'a1' in main.barfoo.
  6333  					// This shouldn't affect 'a1' in main.foobar - we will check that in the next breakpoint.
  6334  					locals = tester.variables(localsScope)
  6335  					checkVarExact(t, locals, -1, "a1", "a1", `"bur"`, "string", noChildren)
  6336  					tester.expectSetVariable(localsScope, "a1", `"fur"`)
  6337  					tester.evaluate("a1", `"fur"`, noChildren)
  6338  					// We will check a1 in main.foobar isn't affected from the next breakpoint.
  6339  
  6340  					client.StackTraceRequest(1, 1, 20)
  6341  					res := client.ExpectStackTraceResponse(t)
  6342  					if len(res.Body.StackFrames) < 1 {
  6343  						t.Fatalf("stack trace response = %#v, wanted at least one stack frame", res)
  6344  					}
  6345  					outerFrame := res.Body.StackFrames[0].Id
  6346  					client.EvaluateRequest("a1", outerFrame, "whatever_context")
  6347  					evalRes := client.ExpectEvaluateResponse(t)
  6348  					checkEval(t, evalRes, `"barbarbar"`, noChildren)
  6349  				},
  6350  				disconnect: true,
  6351  			}})
  6352  	})
  6353  
  6354  	runTest(t, "fncall", func(client *daptest.Client, fixture protest.Fixture) {
  6355  		runDebugSessionWithBPs(t, client, "launch",
  6356  			func() {
  6357  				client.LaunchRequestWithArgs(map[string]interface{}{
  6358  					"mode": "exec", "program": fixture.Path, "showGlobalVariables": true,
  6359  				})
  6360  			},
  6361  			fixture.Source, []int{}, // breakpoints are set within the program.
  6362  			[]onBreakpoint{{
  6363  				// Stop at second breakpoint and set a1.
  6364  				execute: func() {
  6365  					tester := &helperForSetVariable{t, client}
  6366  
  6367  					checkStop(t, client, 1, "main.main", -1)
  6368  
  6369  					_ = tester.variables(localsScope)
  6370  
  6371  					// successful variable set using a function call.
  6372  					tester.expectSetVariable(localsScope, "str", `callstacktrace()`)
  6373  					tester.evaluateRegex("str", `.*in main.callstacktrace at.*`, noChildren)
  6374  
  6375  					tester.failSetVariableAndStop(localsScope, "str", `callpanic()`, `callpanic panicked`)
  6376  					checkStop(t, client, 1, "main.main", -1)
  6377  
  6378  					// breakpoint during a function call.
  6379  					tester.failSetVariableAndStop(localsScope, "str", `callbreak()`, "call stopped")
  6380  
  6381  					// TODO(hyangah): continue after this causes runtime error while resuming
  6382  					// unfinished injected call.
  6383  					//   runtime error: can not convert %!s(<nil>) constant to string
  6384  					// This can be reproducible with dlv cli. (`call str = callbreak(); continue`)
  6385  				},
  6386  				disconnect: true,
  6387  			}})
  6388  	})
  6389  }
  6390  
  6391  func TestOptionalNotYetImplementedResponses(t *testing.T) {
  6392  	var got *dap.ErrorResponse
  6393  	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
  6394  		seqCnt := 1
  6395  		expectNotYetImplemented := func(cmd string) {
  6396  			t.Helper()
  6397  			got = client.ExpectNotYetImplementedErrorResponse(t)
  6398  			if got.RequestSeq != seqCnt || got.Command != cmd {
  6399  				t.Errorf("\ngot  %#v\nwant RequestSeq=%d Command=%s", got, seqCnt, cmd)
  6400  			}
  6401  			seqCnt++
  6402  		}
  6403  
  6404  		client.TerminateRequest()
  6405  		expectNotYetImplemented("terminate")
  6406  
  6407  		client.RestartRequest()
  6408  		expectNotYetImplemented("restart")
  6409  
  6410  		client.SetExpressionRequest()
  6411  		expectNotYetImplemented("setExpression")
  6412  
  6413  		client.LoadedSourcesRequest()
  6414  		expectNotYetImplemented("loadedSources")
  6415  
  6416  		client.ReadMemoryRequest()
  6417  		expectNotYetImplemented("readMemory")
  6418  
  6419  		client.CancelRequest()
  6420  		expectNotYetImplemented("cancel")
  6421  
  6422  		client.DisconnectRequest()
  6423  		client.ExpectDisconnectResponse(t)
  6424  	})
  6425  }
  6426  
  6427  func TestBadLaunchRequests(t *testing.T) {
  6428  	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
  6429  		seqCnt := 1
  6430  		checkFailedToLaunch := func(response *dap.ErrorResponse) {
  6431  			t.Helper()
  6432  			if response.RequestSeq != seqCnt {
  6433  				t.Errorf("RequestSeq got %d, want %d", seqCnt, response.RequestSeq)
  6434  			}
  6435  			if response.Command != "launch" {
  6436  				t.Errorf("Command got %q, want \"launch\"", response.Command)
  6437  			}
  6438  			if response.Message != "Failed to launch" {
  6439  				t.Errorf("Message got %q, want \"Failed to launch\"", response.Message)
  6440  			}
  6441  			if !checkErrorMessageId(response.Body.Error, FailedToLaunch) {
  6442  				t.Errorf("Id got %v, want Id=%d", response.Body.Error, FailedToLaunch)
  6443  			}
  6444  			seqCnt++
  6445  		}
  6446  
  6447  		checkFailedToLaunchWithMessage := func(response *dap.ErrorResponse, errmsg string) {
  6448  			t.Helper()
  6449  			checkFailedToLaunch(response)
  6450  			if !checkErrorMessageFormat(response.Body.Error, errmsg) {
  6451  				t.Errorf("\ngot  %v\nwant Format=%q", response.Body.Error, errmsg)
  6452  			}
  6453  		}
  6454  
  6455  		// Test for the DAP-specific detailed error message.
  6456  		client.LaunchRequest("exec", "", stopOnEntry)
  6457  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6458  			"Failed to launch: The program attribute is missing in debug configuration.")
  6459  
  6460  		// Bad "program"
  6461  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": 12345})
  6462  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6463  			"Failed to launch: invalid debug configuration - cannot unmarshal number into \"program\" of type string")
  6464  
  6465  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": nil})
  6466  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6467  			"Failed to launch: The program attribute is missing in debug configuration.")
  6468  
  6469  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug"})
  6470  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6471  			"Failed to launch: The program attribute is missing in debug configuration.")
  6472  
  6473  		// Bad "mode"
  6474  		client.LaunchRequest("remote", fixture.Path, stopOnEntry)
  6475  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6476  			"Failed to launch: invalid debug configuration - unsupported 'mode' attribute \"remote\"")
  6477  
  6478  		client.LaunchRequest("notamode", fixture.Path, stopOnEntry)
  6479  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6480  			"Failed to launch: invalid debug configuration - unsupported 'mode' attribute \"notamode\"")
  6481  
  6482  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": 12345, "program": fixture.Path})
  6483  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6484  			"Failed to launch: invalid debug configuration - cannot unmarshal number into \"mode\" of type string")
  6485  
  6486  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": ""}) // empty mode defaults to "debug" (not an error)
  6487  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6488  			"Failed to launch: The program attribute is missing in debug configuration.")
  6489  
  6490  		client.LaunchRequestWithArgs(map[string]interface{}{}) // missing mode defaults to "debug" (not an error)
  6491  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6492  			"Failed to launch: The program attribute is missing in debug configuration.")
  6493  
  6494  		// Bad "args"
  6495  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "exec", "program": fixture.Path, "args": "foobar"})
  6496  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6497  			"Failed to launch: invalid debug configuration - cannot unmarshal string into \"args\" of type []string")
  6498  
  6499  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "exec", "program": fixture.Path, "args": 12345})
  6500  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6501  			"Failed to launch: invalid debug configuration - cannot unmarshal number into \"args\" of type []string")
  6502  
  6503  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "exec", "program": fixture.Path, "args": []int{1, 2}})
  6504  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6505  			"Failed to launch: invalid debug configuration - cannot unmarshal number into \"args\" of type string")
  6506  
  6507  		// Bad "buildFlags"
  6508  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "buildFlags": 123})
  6509  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6510  			"Failed to launch: invalid debug configuration - cannot unmarshal number into \"buildFlags\" of type []string or string")
  6511  
  6512  		// Bad "backend"
  6513  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "backend": 123})
  6514  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6515  			"Failed to launch: invalid debug configuration - cannot unmarshal number into \"backend\" of type string")
  6516  
  6517  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "backend": "foo"})
  6518  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6519  			"Failed to launch: could not launch process: unknown backend \"foo\"")
  6520  
  6521  		// Bad "substitutePath"
  6522  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "substitutePath": 123})
  6523  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6524  			"Failed to launch: invalid debug configuration - cannot unmarshal number into \"substitutePath\" of type {\"from\":string, \"to\":string}")
  6525  
  6526  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "substitutePath": []interface{}{123}})
  6527  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6528  			"Failed to launch: invalid debug configuration - cannot use 123 as 'substitutePath' of type {\"from\":string, \"to\":string}")
  6529  
  6530  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "substitutePath": []interface{}{map[string]interface{}{"to": "path2"}}})
  6531  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6532  			"Failed to launch: invalid debug configuration - 'substitutePath' requires both 'from' and 'to' entries")
  6533  
  6534  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "substitutePath": []interface{}{map[string]interface{}{"from": "path1", "to": 123}}})
  6535  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6536  			"Failed to launch: invalid debug configuration - cannot use {\"from\":\"path1\",\"to\":123} as 'substitutePath' of type {\"from\":string, \"to\":string}")
  6537  		// Bad "cwd"
  6538  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "cwd": 123})
  6539  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6540  			"Failed to launch: invalid debug configuration - cannot unmarshal number into \"cwd\" of type string")
  6541  
  6542  		// Skip detailed message checks for potentially different OS-specific errors.
  6543  		client.LaunchRequest("exec", fixture.Path+"_does_not_exist", stopOnEntry)
  6544  		checkFailedToLaunch(client.ExpectVisibleErrorResponse(t)) // No such file or directory
  6545  
  6546  		client.LaunchRequest("debug", fixture.Path+"_does_not_exist", stopOnEntry)
  6547  		oe := client.ExpectOutputEvent(t)
  6548  		if !strings.HasPrefix(oe.Body.Output, "Build Error: ") || oe.Body.Category != "stderr" {
  6549  			t.Errorf("got %#v, want Category=\"stderr\" Output=\"Build Error: ...\"", oe)
  6550  		}
  6551  		checkFailedToLaunch(client.ExpectInvisibleErrorResponse(t))
  6552  
  6553  		client.LaunchRequestWithArgs(map[string]interface{}{
  6554  			"request": "launch",
  6555  			/* mode: debug by default*/
  6556  			"program":     fixture.Path + "_does_not_exist",
  6557  			"stopOnEntry": stopOnEntry,
  6558  		})
  6559  		oe = client.ExpectOutputEvent(t)
  6560  		if !strings.HasPrefix(oe.Body.Output, "Build Error: ") || oe.Body.Category != "stderr" {
  6561  			t.Errorf("got %#v, want Category=\"stderr\" Output=\"Build Error: ...\"", oe)
  6562  		}
  6563  		checkFailedToLaunch(client.ExpectInvisibleErrorResponse(t))
  6564  
  6565  		client.LaunchRequest("exec", fixture.Source, stopOnEntry)
  6566  		checkFailedToLaunch(client.ExpectVisibleErrorResponse(t)) // Not an executable
  6567  
  6568  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "buildFlags": "-bad -flags"})
  6569  		oe = client.ExpectOutputEvent(t)
  6570  		if !strings.HasPrefix(oe.Body.Output, "Build Error: ") || oe.Body.Category != "stderr" {
  6571  			t.Errorf("got %#v, want Category=\"stderr\" Output=\"Build Error: ...\"", oe)
  6572  		}
  6573  		checkFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t), "Failed to launch: Build error: Check the debug console for details.")
  6574  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": true, "buildFlags": "-bad -flags"})
  6575  		oe = client.ExpectOutputEvent(t)
  6576  		if !strings.HasPrefix(oe.Body.Output, "Build Error: ") || oe.Body.Category != "stderr" {
  6577  			t.Errorf("got %#v, want Category=\"stderr\" Output=\"Build Error: ...\"", oe)
  6578  		}
  6579  		checkFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t), "Failed to launch: Build error: Check the debug console for details.")
  6580  
  6581  		// Bad "cwd"
  6582  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": false, "cwd": "dir/invalid"})
  6583  		checkFailedToLaunch(client.ExpectVisibleErrorResponse(t)) // invalid directory, the error message is system-dependent.
  6584  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": true, "cwd": "dir/invalid"})
  6585  		checkFailedToLaunch(client.ExpectVisibleErrorResponse(t)) // invalid directory, the error message is system-dependent.
  6586  
  6587  		// Bad "noDebug"
  6588  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": "true"})
  6589  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), "Failed to launch: invalid debug configuration - cannot unmarshal string into \"noDebug\" of type bool")
  6590  
  6591  		// Bad "replay" parameters
  6592  		// These errors come from dap layer
  6593  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "replay", "traceDirPath": ""})
  6594  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6595  			"Failed to launch: The 'traceDirPath' attribute is missing in debug configuration.")
  6596  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "replay", "program": fixture.Source, "traceDirPath": ""})
  6597  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6598  			"Failed to launch: The 'traceDirPath' attribute is missing in debug configuration.")
  6599  		// These errors come from debugger layer
  6600  		if _, err := exec.LookPath("rr"); err != nil {
  6601  			client.LaunchRequestWithArgs(map[string]interface{}{"mode": "replay", "backend": "ignored", "traceDirPath": ".."})
  6602  			checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6603  				"Failed to launch: backend unavailable")
  6604  		}
  6605  
  6606  		// Bad "core" parameters
  6607  		// These errors come from dap layer
  6608  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "core", "coreFilePath": ""})
  6609  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6610  			"Failed to launch: The program attribute is missing in debug configuration.")
  6611  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "core", "program": fixture.Source, "coreFilePath": ""})
  6612  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6613  			"Failed to launch: The 'coreFilePath' attribute is missing in debug configuration.")
  6614  		// These errors come from debugger layer
  6615  		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "core", "backend": "ignored", "program": fixture.Source, "coreFilePath": fixture.Source})
  6616  		checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t),
  6617  			"Failed to launch: unrecognized core format")
  6618  
  6619  		// We failed to launch the program. Make sure shutdown still works.
  6620  		client.DisconnectRequest()
  6621  		dresp := client.ExpectDisconnectResponse(t)
  6622  		if dresp.RequestSeq != seqCnt {
  6623  			t.Errorf("got %#v, want RequestSeq=%d", dresp, seqCnt)
  6624  		}
  6625  	})
  6626  }
  6627  
  6628  func TestBadAttachRequest(t *testing.T) {
  6629  	runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
  6630  		seqCnt := 1
  6631  		checkFailedToAttach := func(response *dap.ErrorResponse) {
  6632  			t.Helper()
  6633  			if response.RequestSeq != seqCnt {
  6634  				t.Errorf("RequestSeq got %d, want %d", seqCnt, response.RequestSeq)
  6635  			}
  6636  			if response.Command != "attach" {
  6637  				t.Errorf("Command got %q, want \"attach\"", response.Command)
  6638  			}
  6639  			if response.Message != "Failed to attach" {
  6640  				t.Errorf("Message got %q, want \"Failed to attach\"", response.Message)
  6641  			}
  6642  			if !checkErrorMessageId(response.Body.Error, FailedToAttach) {
  6643  				t.Errorf("Id got %v, want %d", response.Body.Error, FailedToAttach)
  6644  			}
  6645  			seqCnt++
  6646  		}
  6647  
  6648  		checkFailedToAttachWithMessage := func(response *dap.ErrorResponse, errmsg string) {
  6649  			t.Helper()
  6650  			checkFailedToAttach(response)
  6651  			if !checkErrorMessageFormat(response.Body.Error, errmsg) {
  6652  				t.Errorf("\ngot  %v\nwant Format=%q", response.Body.Error, errmsg)
  6653  			}
  6654  		}
  6655  
  6656  		// Bad "mode"
  6657  		client.AttachRequest(map[string]interface{}{"mode": "blah blah blah"})
  6658  		checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
  6659  			"Failed to attach: invalid debug configuration - unsupported 'mode' attribute \"blah blah blah\"")
  6660  
  6661  		client.AttachRequest(map[string]interface{}{"mode": 123})
  6662  		checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
  6663  			"Failed to attach: invalid debug configuration - cannot unmarshal number into \"mode\" of type string")
  6664  
  6665  		client.AttachRequest(map[string]interface{}{"mode": ""}) // empty mode defaults to "local" (not an error)
  6666  		checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
  6667  			"Failed to attach: The 'processId' or 'waitFor' attribute is missing in debug configuration")
  6668  
  6669  		client.AttachRequest(map[string]interface{}{}) // no mode defaults to "local" (not an error)
  6670  		checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
  6671  			"Failed to attach: The 'processId' or 'waitFor' attribute is missing in debug configuration")
  6672  
  6673  		// Bad "processId"
  6674  		client.AttachRequest(map[string]interface{}{"mode": "local"})
  6675  		checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
  6676  			"Failed to attach: The 'processId' or 'waitFor' attribute is missing in debug configuration")
  6677  
  6678  		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": nil})
  6679  		checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
  6680  			"Failed to attach: The 'processId' or 'waitFor' attribute is missing in debug configuration")
  6681  
  6682  		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 0})
  6683  		checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
  6684  			"Failed to attach: The 'processId' or 'waitFor' attribute is missing in debug configuration")
  6685  
  6686  		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 1, "waitFor": "loopprog"})
  6687  		checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
  6688  			"Failed to attach: 'processId' and 'waitFor' are mutually exclusive, and can't be specified at the same time")
  6689  
  6690  		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": "1"})
  6691  		checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
  6692  			"Failed to attach: invalid debug configuration - cannot unmarshal string into \"processId\" of type int")
  6693  
  6694  		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 1})
  6695  		// The exact message varies on different systems, so skip that check
  6696  		checkFailedToAttach(client.ExpectVisibleErrorResponse(t)) // could not attach to pid 1
  6697  
  6698  		// This will make debugger.(*Debugger) panic, which we will catch as an internal error.
  6699  		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": -1})
  6700  		er := client.ExpectInvisibleErrorResponse(t)
  6701  		if er.RequestSeq != seqCnt {
  6702  			t.Errorf("RequestSeq got %d, want %d", seqCnt, er.RequestSeq)
  6703  		}
  6704  		seqCnt++
  6705  		if er.Command != "" {
  6706  			t.Errorf("Command got %q, want \"attach\"", er.Command)
  6707  		}
  6708  		if !checkErrorMessageFormat(er.Body.Error, "Internal Error: runtime error: index out of range [0] with length 0") {
  6709  			t.Errorf("Message got %q, want \"Internal Error: runtime error: index out of range [0] with length 0\"", er.Message)
  6710  		}
  6711  		if !checkErrorMessageId(er.Body.Error, InternalError) {
  6712  			t.Errorf("Id got %v, want Id=%d", er.Body.Error, InternalError)
  6713  		}
  6714  
  6715  		// Bad "backend"
  6716  		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 1, "backend": 123})
  6717  		checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
  6718  			"Failed to attach: invalid debug configuration - cannot unmarshal number into \"backend\" of type string")
  6719  
  6720  		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 1, "backend": "foo"})
  6721  		checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t),
  6722  			"Failed to attach: could not attach to pid 1: unknown backend \"foo\"")
  6723  
  6724  		// We failed to attach to the program. Make sure shutdown still works.
  6725  		client.DisconnectRequest()
  6726  		dresp := client.ExpectDisconnectResponse(t)
  6727  		if dresp.RequestSeq != seqCnt {
  6728  			t.Errorf("got %#v, want RequestSeq=%d", dresp, seqCnt)
  6729  		}
  6730  	})
  6731  }
  6732  
  6733  func launchDebuggerWithTargetRunning(t *testing.T, fixture string) (*protest.Fixture, *debugger.Debugger) {
  6734  	t.Helper()
  6735  	fixbin, dbg := launchDebuggerWithTargetHalted(t, fixture)
  6736  	running := make(chan struct{})
  6737  	var err error
  6738  	go func() {
  6739  		t.Helper()
  6740  		_, err = dbg.Command(&api.DebuggerCommand{Name: api.Continue}, running)
  6741  		select {
  6742  		case <-running:
  6743  		default:
  6744  			close(running)
  6745  		}
  6746  	}()
  6747  	<-running
  6748  	if err != nil {
  6749  		t.Fatal("failed to continue on launch", err)
  6750  	}
  6751  	return fixbin, dbg
  6752  }
  6753  
  6754  func launchDebuggerWithTargetHalted(t *testing.T, fixture string) (*protest.Fixture, *debugger.Debugger) {
  6755  	t.Helper()
  6756  	fixbin := protest.BuildFixture(fixture, protest.AllNonOptimized)
  6757  	cfg := service.Config{
  6758  		ProcessArgs: []string{fixbin.Path},
  6759  		Debugger:    debugger.Config{Backend: "default"},
  6760  	}
  6761  	dbg, err := debugger.New(&cfg.Debugger, cfg.ProcessArgs) // debugger halts process on entry
  6762  	if err != nil {
  6763  		t.Fatal("failed to start debugger:", err)
  6764  	}
  6765  	return &fixbin, dbg
  6766  }
  6767  
  6768  func attachDebuggerWithTargetHalted(t *testing.T, fixture string) (*exec.Cmd, *debugger.Debugger) {
  6769  	t.Helper()
  6770  	fixbin := protest.BuildFixture(fixture, protest.AllNonOptimized)
  6771  	cmd := execFixture(t, fixbin)
  6772  	cfg := service.Config{Debugger: debugger.Config{Backend: "default", AttachPid: cmd.Process.Pid}}
  6773  	dbg, err := debugger.New(&cfg.Debugger, nil) // debugger halts process on entry
  6774  	if err != nil {
  6775  		t.Fatal("failed to start debugger:", err)
  6776  	}
  6777  	return cmd, dbg
  6778  }
  6779  
  6780  // runTestWithDebugger starts the server and sets its debugger, initializes a debug session,
  6781  // runs test, then disconnects. Expects no running async handler at the end of test() (either
  6782  // process is halted or debug session never launched.)
  6783  func runTestWithDebugger(t *testing.T, dbg *debugger.Debugger, test func(c *daptest.Client)) {
  6784  	serverStopped := make(chan struct{})
  6785  	server, _ := startDAPServer(t, false, serverStopped)
  6786  	client := daptest.NewClient(server.listener.Addr().String())
  6787  	time.Sleep(100 * time.Millisecond) // Give time for connection to be set as dap.Session
  6788  	server.sessionMu.Lock()
  6789  	if server.session == nil {
  6790  		t.Fatal("DAP session is not ready")
  6791  	}
  6792  	// Mock dap.NewSession arguments, so
  6793  	// this dap.Server can be used as a proxy for
  6794  	// rpccommon.Server running dap.Session.
  6795  	server.session.config.Debugger.AttachPid = dbg.AttachPid()
  6796  	server.session.debugger = dbg
  6797  	server.sessionMu.Unlock()
  6798  	defer client.Close()
  6799  	client.InitializeRequest()
  6800  	client.ExpectInitializeResponseAndCapabilities(t)
  6801  
  6802  	test(client)
  6803  
  6804  	client.DisconnectRequest()
  6805  	if dbg.AttachPid() == 0 { // launched target
  6806  		client.ExpectOutputEventDetachingKill(t)
  6807  	} else { // attached to target
  6808  		client.ExpectOutputEventDetachingNoKill(t)
  6809  	}
  6810  	client.ExpectDisconnectResponse(t)
  6811  	client.ExpectTerminatedEvent(t)
  6812  
  6813  	<-serverStopped
  6814  }
  6815  
  6816  func TestAttachRemoteToDlvLaunchHaltedStopOnEntry(t *testing.T) {
  6817  	// Halted + stop on entry
  6818  	_, dbg := launchDebuggerWithTargetHalted(t, "increment")
  6819  	runTestWithDebugger(t, dbg, func(client *daptest.Client) {
  6820  		client.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": true})
  6821  		client.ExpectInitializedEvent(t)
  6822  		client.ExpectAttachResponse(t)
  6823  		client.ConfigurationDoneRequest()
  6824  		client.ExpectStoppedEvent(t)
  6825  		client.ExpectConfigurationDoneResponse(t)
  6826  	})
  6827  }
  6828  
  6829  func TestAttachRemoteToDlvAttachHaltedStopOnEntry(t *testing.T) {
  6830  	cmd, dbg := attachDebuggerWithTargetHalted(t, "http_server")
  6831  	runTestWithDebugger(t, dbg, func(client *daptest.Client) {
  6832  		client.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": true})
  6833  		client.ExpectCapabilitiesEventSupportTerminateDebuggee(t)
  6834  		client.ExpectInitializedEvent(t)
  6835  		client.ExpectAttachResponse(t)
  6836  		client.ConfigurationDoneRequest()
  6837  		client.ExpectStoppedEvent(t)
  6838  		client.ExpectConfigurationDoneResponse(t)
  6839  	})
  6840  	cmd.Process.Kill()
  6841  }
  6842  
  6843  func TestAttachRemoteToHaltedTargetContinueOnEntry(t *testing.T) {
  6844  	// Halted + continue on entry
  6845  	_, dbg := launchDebuggerWithTargetHalted(t, "http_server")
  6846  	runTestWithDebugger(t, dbg, func(client *daptest.Client) {
  6847  		client.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": false})
  6848  		client.ExpectInitializedEvent(t)
  6849  		client.ExpectAttachResponse(t)
  6850  		client.ConfigurationDoneRequest()
  6851  		client.ExpectConfigurationDoneResponse(t)
  6852  		// Continuing
  6853  		time.Sleep(time.Second)
  6854  		// Halt to make the disconnect sequence more predictable.
  6855  		client.PauseRequest(1)
  6856  		expectPauseResponseAndStoppedEvent(t, client)
  6857  	})
  6858  }
  6859  
  6860  func TestAttachRemoteToRunningTargetStopOnEntry(t *testing.T) {
  6861  	fixture, dbg := launchDebuggerWithTargetRunning(t, "loopprog")
  6862  	runTestWithDebugger(t, dbg, func(client *daptest.Client) {
  6863  		client.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": true})
  6864  		client.ExpectInitializedEvent(t)
  6865  		client.ExpectAttachResponse(t)
  6866  		// Target is halted here
  6867  		client.SetBreakpointsRequest(fixture.Source, []int{8})
  6868  		expectSetBreakpointsResponse(t, client, []Breakpoint{{8, fixture.Source, true, ""}})
  6869  		client.ConfigurationDoneRequest()
  6870  		client.ExpectStoppedEvent(t)
  6871  		client.ExpectConfigurationDoneResponse(t)
  6872  		client.ContinueRequest(1)
  6873  		client.ExpectContinueResponse(t)
  6874  		client.ExpectStoppedEvent(t)
  6875  		checkStop(t, client, 1, "main.loop", 8)
  6876  	})
  6877  }
  6878  
  6879  func TestAttachRemoteToRunningTargetContinueOnEntry(t *testing.T) {
  6880  	fixture, dbg := launchDebuggerWithTargetRunning(t, "loopprog")
  6881  	runTestWithDebugger(t, dbg, func(client *daptest.Client) {
  6882  		client.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": false})
  6883  		client.ExpectInitializedEvent(t)
  6884  		client.ExpectAttachResponse(t)
  6885  		// Target is halted here
  6886  		client.SetBreakpointsRequest(fixture.Source, []int{8})
  6887  		expectSetBreakpointsResponse(t, client, []Breakpoint{{8, fixture.Source, true, ""}})
  6888  		client.ConfigurationDoneRequest()
  6889  		// Target is restarted here
  6890  		client.ExpectConfigurationDoneResponse(t)
  6891  		client.ExpectStoppedEvent(t)
  6892  		checkStop(t, client, 1, "main.loop", 8)
  6893  	})
  6894  }
  6895  
  6896  // MultiClientCloseServerMock mocks the rpccommon.Server using a dap.Server to exercise
  6897  // the shutdown logic in dap.Session where it does NOT take down the server on close
  6898  // in multi-client mode. (The dap mode of the rpccommon.Server is tested in dlv_test).
  6899  // The dap.Server is a single-use server. Once its one and only session is closed,
  6900  // the server and the target must be taken down manually for the test not to leak.
  6901  type MultiClientCloseServerMock struct {
  6902  	impl      *Server
  6903  	debugger  *debugger.Debugger
  6904  	forceStop chan struct{}
  6905  	stopped   chan struct{}
  6906  }
  6907  
  6908  func NewMultiClientCloseServerMock(t *testing.T, fixture string) *MultiClientCloseServerMock {
  6909  	var s MultiClientCloseServerMock
  6910  	s.stopped = make(chan struct{})
  6911  	s.impl, s.forceStop = startDAPServer(t, false, s.stopped)
  6912  	_, s.debugger = launchDebuggerWithTargetHalted(t, "http_server")
  6913  	return &s
  6914  }
  6915  
  6916  func (s *MultiClientCloseServerMock) acceptNewClient(t *testing.T) *daptest.Client {
  6917  	client := daptest.NewClient(s.impl.listener.Addr().String())
  6918  	time.Sleep(100 * time.Millisecond) // Give time for connection to be set as dap.Session
  6919  	s.impl.sessionMu.Lock()
  6920  	if s.impl.session == nil {
  6921  		t.Fatal("dap session is not ready")
  6922  	}
  6923  	// A dap.Server doesn't support accept-multiclient, but we can use this
  6924  	// hack to test the inner connection logic that is used by a server that does.
  6925  	s.impl.session.config.AcceptMulti = true
  6926  	s.impl.session.debugger = s.debugger
  6927  	s.impl.sessionMu.Unlock()
  6928  	return client
  6929  }
  6930  
  6931  func (s *MultiClientCloseServerMock) stop(t *testing.T) {
  6932  	close(s.forceStop)
  6933  	// If the server doesn't have an active session,
  6934  	// closing it would leak the debugger with the target because
  6935  	// they are part of dap.Session.
  6936  	// We must take it down manually as if we are in rpccommon::ServerImpl::Stop.
  6937  	if s.debugger.IsRunning() {
  6938  		s.debugger.Command(&api.DebuggerCommand{Name: api.Halt}, nil)
  6939  	}
  6940  	s.debugger.Detach(true)
  6941  }
  6942  
  6943  func (s *MultiClientCloseServerMock) verifyStopped(t *testing.T) {
  6944  	if state, err := s.debugger.State(true /*nowait*/); err != proc.ErrProcessDetached && !processExited(state, err) {
  6945  		t.Errorf("target leak")
  6946  	}
  6947  	verifyServerStopped(t, s.impl)
  6948  }
  6949  
  6950  // TestAttachRemoteMultiClientDisconnect tests that remote attach doesn't take down
  6951  // the server in multi-client mode unless terminateDebuggee is explicitly set.
  6952  func TestAttachRemoteMultiClientDisconnect(t *testing.T) {
  6953  	closingClientSessionOnly := fmt.Sprintf(daptest.ClosingClient, "halted")
  6954  	detachingAndTerminating := "Detaching and terminating target process"
  6955  	tests := []struct {
  6956  		name              string
  6957  		disconnectRequest func(client *daptest.Client)
  6958  		expect            string
  6959  	}{
  6960  		{"default", func(c *daptest.Client) { c.DisconnectRequest() }, closingClientSessionOnly},
  6961  		{"terminate=true", func(c *daptest.Client) { c.DisconnectRequestWithKillOption(true) }, detachingAndTerminating},
  6962  		{"terminate=false", func(c *daptest.Client) { c.DisconnectRequestWithKillOption(false) }, closingClientSessionOnly},
  6963  	}
  6964  	for _, tc := range tests {
  6965  		t.Run(tc.name, func(t *testing.T) {
  6966  			server := NewMultiClientCloseServerMock(t, "increment")
  6967  			client := server.acceptNewClient(t)
  6968  			defer client.Close()
  6969  
  6970  			client.InitializeRequest()
  6971  			client.ExpectInitializeResponseAndCapabilities(t)
  6972  
  6973  			client.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": true})
  6974  			client.ExpectCapabilitiesEventSupportTerminateDebuggee(t)
  6975  			client.ExpectInitializedEvent(t)
  6976  			client.ExpectAttachResponse(t)
  6977  			client.ConfigurationDoneRequest()
  6978  			client.ExpectStoppedEvent(t)
  6979  			client.ExpectConfigurationDoneResponse(t)
  6980  
  6981  			tc.disconnectRequest(client)
  6982  			e := client.ExpectOutputEvent(t)
  6983  			if matched, _ := regexp.MatchString(tc.expect, e.Body.Output); !matched {
  6984  				t.Errorf("\ngot %#v\nwant Output=%q", e, tc.expect)
  6985  			}
  6986  			client.ExpectDisconnectResponse(t)
  6987  			client.ExpectTerminatedEvent(t)
  6988  			time.Sleep(10 * time.Millisecond) // give time for things to shut down
  6989  
  6990  			if tc.expect == closingClientSessionOnly {
  6991  				// At this point a multi-client server is still running. but session should be done.
  6992  				verifySessionStopped(t, server.impl.session)
  6993  				// Verify target's running state.
  6994  				if server.debugger.IsRunning() {
  6995  					t.Errorf("\ngot running=true, want false")
  6996  				}
  6997  				server.stop(t)
  6998  			}
  6999  			<-server.stopped
  7000  			server.verifyStopped(t)
  7001  		})
  7002  	}
  7003  }
  7004  
  7005  func TestLaunchAttachErrorWhenDebugInProgress(t *testing.T) {
  7006  	tests := []struct {
  7007  		name string
  7008  		dbg  func() *debugger.Debugger
  7009  	}{
  7010  		{"halted", func() *debugger.Debugger { _, dbg := launchDebuggerWithTargetHalted(t, "increment"); return dbg }},
  7011  		{"running", func() *debugger.Debugger { _, dbg := launchDebuggerWithTargetRunning(t, "loopprog"); return dbg }},
  7012  	}
  7013  	for _, tc := range tests {
  7014  		t.Run(tc.name, func(t *testing.T) {
  7015  			runTestWithDebugger(t, tc.dbg(), func(client *daptest.Client) {
  7016  				client.EvaluateRequest("1==1", 0 /*no frame specified*/, "repl")
  7017  				if tc.name == "running" {
  7018  					client.ExpectInvisibleErrorResponse(t)
  7019  				} else {
  7020  					client.ExpectEvaluateResponse(t)
  7021  				}
  7022  
  7023  				// Both launch and attach requests should go through for additional error checking
  7024  				client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 100})
  7025  				er := client.ExpectVisibleErrorResponse(t)
  7026  				msgRe := regexp.MustCompile("Failed to attach: debug session already in progress at .+ - use remote mode to connect to a server with an active debug session")
  7027  				if er.Body.Error == nil || er.Body.Error.Id != FailedToAttach || !msgRe.MatchString(er.Body.Error.Format) {
  7028  					t.Errorf("got %#v, want Id=%d Format=%q", er.Body.Error, FailedToAttach, msgRe)
  7029  				}
  7030  				tests := []string{"debug", "test", "exec", "replay", "core"}
  7031  				for _, mode := range tests {
  7032  					t.Run(mode, func(t *testing.T) {
  7033  						client.LaunchRequestWithArgs(map[string]interface{}{"mode": mode})
  7034  						er := client.ExpectVisibleErrorResponse(t)
  7035  						msgRe := regexp.MustCompile("Failed to launch: debug session already in progress at .+ - use remote attach mode to connect to a server with an active debug session")
  7036  						if er.Body.Error == nil || er.Body.Error.Id != FailedToLaunch || !msgRe.MatchString(er.Body.Error.Format) {
  7037  							t.Errorf("got %#v, want Id=%d Format=%q", er.Body.Error, FailedToLaunch, msgRe)
  7038  						}
  7039  					})
  7040  				}
  7041  			})
  7042  		})
  7043  	}
  7044  }
  7045  
  7046  func TestBadInitializeRequest(t *testing.T) {
  7047  	runInitializeTest := func(args dap.InitializeRequestArguments, err string) {
  7048  		t.Helper()
  7049  		// Only one initialize request is allowed, so use a new server
  7050  		// for each test.
  7051  		serverStopped := make(chan struct{})
  7052  		client := startDAPServerWithClient(t, false, serverStopped)
  7053  		defer client.Close()
  7054  
  7055  		client.InitializeRequestWithArgs(args)
  7056  		response := client.ExpectErrorResponse(t)
  7057  		if response.Command != "initialize" {
  7058  			t.Errorf("Command got %q, want \"launch\"", response.Command)
  7059  		}
  7060  		if response.Message != "Failed to initialize" {
  7061  			t.Errorf("Message got %q, want \"Failed to launch\"", response.Message)
  7062  		}
  7063  		if !checkErrorMessageId(response.Body.Error, FailedToInitialize) {
  7064  			t.Errorf("Id got %v, want Id=%d", response.Body.Error, FailedToInitialize)
  7065  		}
  7066  		if !checkErrorMessageFormat(response.Body.Error, err) {
  7067  			t.Errorf("\ngot  %q\nwant %q", response.Body.Error.Format, err)
  7068  		}
  7069  
  7070  		client.DisconnectRequest()
  7071  		client.ExpectDisconnectResponse(t)
  7072  		<-serverStopped
  7073  	}
  7074  
  7075  	// Bad path format.
  7076  	runInitializeTest(dap.InitializeRequestArguments{
  7077  		AdapterID:       "go",
  7078  		PathFormat:      "url", // unsupported 'pathFormat'
  7079  		LinesStartAt1:   true,
  7080  		ColumnsStartAt1: true,
  7081  		Locale:          "en-us",
  7082  	},
  7083  		"Failed to initialize: Unsupported 'pathFormat' value 'url'.",
  7084  	)
  7085  
  7086  	// LinesStartAt1 must be true.
  7087  	runInitializeTest(dap.InitializeRequestArguments{
  7088  		AdapterID:       "go",
  7089  		PathFormat:      "path",
  7090  		LinesStartAt1:   false, // only 1-based line numbers are supported
  7091  		ColumnsStartAt1: true,
  7092  		Locale:          "en-us",
  7093  	},
  7094  		"Failed to initialize: Only 1-based line numbers are supported.",
  7095  	)
  7096  
  7097  	// ColumnsStartAt1 must be true.
  7098  	runInitializeTest(dap.InitializeRequestArguments{
  7099  		AdapterID:       "go",
  7100  		PathFormat:      "path",
  7101  		LinesStartAt1:   true,
  7102  		ColumnsStartAt1: false, // only 1-based column numbers are supported
  7103  		Locale:          "en-us",
  7104  	},
  7105  		"Failed to initialize: Only 1-based column numbers are supported.",
  7106  	)
  7107  }
  7108  
  7109  func TestBadlyFormattedMessageToServer(t *testing.T) {
  7110  	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
  7111  		// Send a badly formatted message to the server, and expect it to close the
  7112  		// connection.
  7113  		client.BadRequest()
  7114  		time.Sleep(100 * time.Millisecond)
  7115  
  7116  		_, err := client.ReadMessage()
  7117  
  7118  		if err != io.EOF {
  7119  			t.Errorf("got err=%v, want io.EOF", err)
  7120  		}
  7121  	})
  7122  	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
  7123  		// Send an unknown request message to the server, and expect it to send
  7124  		// an error response.
  7125  		client.UnknownRequest()
  7126  		err := client.ExpectErrorResponse(t)
  7127  		if !checkErrorMessageFormat(err.Body.Error, "Internal Error: Request command 'unknown' is not supported (seq: 1)") || err.RequestSeq != 1 {
  7128  			t.Errorf("got %v, want  RequestSeq=1 Error=\"Internal Error: Request command 'unknown' is not supported (seq: 1)\"", err)
  7129  		}
  7130  
  7131  		// Make sure that the unknown request did not kill the server.
  7132  		client.InitializeRequest()
  7133  		client.ExpectInitializeResponse(t)
  7134  
  7135  		client.DisconnectRequest()
  7136  		client.ExpectDisconnectResponse(t)
  7137  	})
  7138  }
  7139  
  7140  func TestParseLogPoint(t *testing.T) {
  7141  	tests := []struct {
  7142  		name           string
  7143  		msg            string
  7144  		wantTracepoint bool
  7145  		wantFormat     string
  7146  		wantArgs       []string
  7147  		wantErr        bool
  7148  	}{
  7149  		// Test simple log messages.
  7150  		{name: "simple string", msg: "hello, world!", wantTracepoint: true, wantFormat: "hello, world!"},
  7151  		{name: "empty string", msg: "", wantTracepoint: false, wantErr: false},
  7152  		// Test parse eval expressions.
  7153  		{
  7154  			name:           "simple eval",
  7155  			msg:            "{x}",
  7156  			wantTracepoint: true,
  7157  			wantFormat:     "%s",
  7158  			wantArgs:       []string{"x"},
  7159  		},
  7160  		{
  7161  			name:           "type cast",
  7162  			msg:            "hello {string(x)}",
  7163  			wantTracepoint: true,
  7164  			wantFormat:     "hello %s",
  7165  			wantArgs:       []string{"string(x)"},
  7166  		},
  7167  		{
  7168  			name:           "multiple eval",
  7169  			msg:            "{x} {y} {z}",
  7170  			wantTracepoint: true,
  7171  			wantFormat:     "%s %s %s",
  7172  			wantArgs:       []string{"x", "y", "z"},
  7173  		},
  7174  		{
  7175  			name:           "eval expressions contain braces",
  7176  			msg:            "{interface{}(x)} {myType{y}} {[]myType{{z}}}",
  7177  			wantTracepoint: true,
  7178  			wantFormat:     "%s %s %s",
  7179  			wantArgs:       []string{"interface{}(x)", "myType{y}", "[]myType{{z}}"},
  7180  		},
  7181  		// Test parse errors.
  7182  		{name: "empty evaluation", msg: "{}", wantErr: true},
  7183  		{name: "empty space evaluation", msg: "{   \n}", wantErr: true},
  7184  		{name: "open brace missing closed", msg: "{", wantErr: true},
  7185  		{name: "closed brace missing open", msg: "}", wantErr: true},
  7186  		{name: "open brace in expression", msg: `{m["{"]}`, wantErr: true},
  7187  		{name: "closed brace in expression", msg: `{m["}"]}`, wantErr: true},
  7188  	}
  7189  	for _, tt := range tests {
  7190  		t.Run(tt.name, func(t *testing.T) {
  7191  			gotTracepoint, gotLogMessage, err := parseLogPoint(tt.msg)
  7192  			if gotTracepoint != tt.wantTracepoint {
  7193  				t.Errorf("parseLogPoint() tracepoint = %v, wantTracepoint %v", gotTracepoint, tt.wantTracepoint)
  7194  				return
  7195  			}
  7196  			if (err != nil) != tt.wantErr {
  7197  				t.Errorf("parseLogPoint() error = %v, wantErr %v", err, tt.wantErr)
  7198  				return
  7199  			}
  7200  			if !tt.wantTracepoint {
  7201  				return
  7202  			}
  7203  			if gotLogMessage == nil {
  7204  				t.Errorf("parseLogPoint() gotLogMessage = nil, want log message")
  7205  				return
  7206  			}
  7207  			if gotLogMessage.format != tt.wantFormat {
  7208  				t.Errorf("parseLogPoint() gotFormat = %v, want %v", gotLogMessage.format, tt.wantFormat)
  7209  			}
  7210  			if !reflect.DeepEqual(gotLogMessage.args, tt.wantArgs) {
  7211  				t.Errorf("parseLogPoint() gotArgs = %v, want %v", gotLogMessage.args, tt.wantArgs)
  7212  			}
  7213  		})
  7214  	}
  7215  }
  7216  
  7217  func TestDisassemble(t *testing.T) {
  7218  	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
  7219  		runDebugSessionWithBPs(t, client, "launch",
  7220  			// Launch
  7221  			func() {
  7222  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  7223  			},
  7224  			// Set breakpoints
  7225  			fixture.Source, []int{17},
  7226  			[]onBreakpoint{{
  7227  				// Stop at line 17
  7228  				execute: func() {
  7229  					checkStop(t, client, 1, "main.main", 17)
  7230  
  7231  					client.StackTraceRequest(1, 0, 1)
  7232  					st := client.ExpectStackTraceResponse(t)
  7233  					if len(st.Body.StackFrames) < 1 {
  7234  						t.Fatalf("\ngot  %#v\nwant len(stackframes) => 1", st)
  7235  					}
  7236  					// Request the single instruction that the program is stopped at.
  7237  					pc := st.Body.StackFrames[0].InstructionPointerReference
  7238  					client.DisassembleRequest(pc, 0, 1)
  7239  					dr := client.ExpectDisassembleResponse(t)
  7240  					if len(dr.Body.Instructions) != 1 {
  7241  						t.Errorf("\ngot %#v\nwant len(instructions) = 1", dr)
  7242  					} else if dr.Body.Instructions[0].Address != pc {
  7243  						t.Errorf("\ngot %#v\nwant instructions[0].Address = %s", dr, pc)
  7244  					}
  7245  
  7246  					// Request the instruction that the program is stopped at, and the two
  7247  					// surrounding it.
  7248  					client.DisassembleRequest(pc, -1, 3)
  7249  					dr = client.ExpectDisassembleResponse(t)
  7250  					if len(dr.Body.Instructions) != 3 {
  7251  						t.Errorf("\ngot %#v\nwant len(instructions) = 3", dr)
  7252  					} else if dr.Body.Instructions[1].Address != pc {
  7253  						t.Errorf("\ngot %#v\nwant instructions[1].Address = %s", dr, pc)
  7254  					}
  7255  
  7256  					// Request zero instructions.
  7257  					client.DisassembleRequest(pc, 0, 0)
  7258  					dr = client.ExpectDisassembleResponse(t)
  7259  					if len(dr.Body.Instructions) != 0 {
  7260  						t.Errorf("\ngot %#v\nwant len(instructions) = 0", dr)
  7261  					}
  7262  
  7263  					// Request invalid instructions.
  7264  					checkInvalidInstruction := func(instructions []dap.DisassembledInstruction, count int, address uint64) {
  7265  						if len(instructions) != count {
  7266  							t.Errorf("\ngot %#v\nwant len(instructions) = %d", dr, count)
  7267  						}
  7268  						for i, got := range instructions {
  7269  							if got.Instruction != invalidInstruction.Instruction {
  7270  								t.Errorf("\ngot [%d].Instruction=%q\nwant = %#v", i, got.Instruction, invalidInstruction.Address)
  7271  							}
  7272  							addr, err := strconv.ParseUint(got.Address, 0, 64)
  7273  							if err != nil {
  7274  								t.Error(err)
  7275  								continue
  7276  							}
  7277  							if addr != address {
  7278  								t.Errorf("\ngot [%d].Address=%s\nwant = %#x", i, got.Address, address)
  7279  							}
  7280  						}
  7281  					}
  7282  					client.DisassembleRequest("0x0", 0, 10)
  7283  					checkInvalidInstruction(client.ExpectDisassembleResponse(t).Body.Instructions, 10, 0)
  7284  
  7285  					client.DisassembleRequest(fmt.Sprintf("%#x", uint64(math.MaxUint64)), 0, 10)
  7286  					checkInvalidInstruction(client.ExpectDisassembleResponse(t).Body.Instructions, 10, uint64(math.MaxUint64))
  7287  
  7288  					// Bad request, not a number.
  7289  					client.DisassembleRequest("hello, world!", 0, 1)
  7290  					client.ExpectErrorResponse(t)
  7291  
  7292  					// Bad request, not an address in program.
  7293  					client.DisassembleRequest("0x5", 0, 100)
  7294  					client.ExpectErrorResponse(t)
  7295  				},
  7296  				disconnect: true,
  7297  			}},
  7298  		)
  7299  	})
  7300  }
  7301  
  7302  func TestAlignPCs(t *testing.T) {
  7303  	NUM_FUNCS := 10
  7304  	// Create fake functions to test align PCs.
  7305  	funcs := make([]proc.Function, NUM_FUNCS)
  7306  	for i := 0; i < len(funcs); i++ {
  7307  		funcs[i] = proc.Function{
  7308  			Entry: uint64(100 + i*10),
  7309  			End:   uint64(100 + i*10 + 5),
  7310  		}
  7311  	}
  7312  	bi := &proc.BinaryInfo{
  7313  		Functions: funcs,
  7314  	}
  7315  	type args struct {
  7316  		start uint64
  7317  		end   uint64
  7318  	}
  7319  	tests := []struct {
  7320  		name      string
  7321  		args      args
  7322  		wantStart uint64
  7323  		wantEnd   uint64
  7324  	}{
  7325  		{
  7326  			name: "out of bounds",
  7327  			args: args{
  7328  				start: funcs[0].Entry - 5,
  7329  				end:   funcs[NUM_FUNCS-1].End + 5,
  7330  			},
  7331  			wantStart: funcs[0].Entry,         // start of first function
  7332  			wantEnd:   funcs[NUM_FUNCS-1].End, // end of last function
  7333  		},
  7334  		{
  7335  			name: "same function",
  7336  			args: args{
  7337  				start: funcs[1].Entry + 1,
  7338  				end:   funcs[1].Entry + 2,
  7339  			},
  7340  			wantStart: funcs[1].Entry, // start of containing function
  7341  			wantEnd:   funcs[1].End,   // end of containing function
  7342  		},
  7343  		{
  7344  			name: "between functions",
  7345  			args: args{
  7346  				start: funcs[1].End + 1,
  7347  				end:   funcs[1].End + 2,
  7348  			},
  7349  			wantStart: funcs[1].Entry, // start of function before
  7350  			wantEnd:   funcs[2].Entry, // start of function after
  7351  		},
  7352  		{
  7353  			name: "start of function",
  7354  			args: args{
  7355  				start: funcs[2].Entry,
  7356  				end:   funcs[5].Entry,
  7357  			},
  7358  			wantStart: funcs[2].Entry, // start of current function
  7359  			wantEnd:   funcs[5].End,   // end of current function
  7360  		},
  7361  		{
  7362  			name: "end of function",
  7363  			args: args{
  7364  				start: funcs[4].End,
  7365  				end:   funcs[8].End,
  7366  			},
  7367  			wantStart: funcs[4].Entry, // start of current function
  7368  			wantEnd:   funcs[9].Entry, // start of next function
  7369  		},
  7370  	}
  7371  	for _, tt := range tests {
  7372  		t.Run(tt.name, func(t *testing.T) {
  7373  			gotStart, gotEnd := alignPCs(bi, tt.args.start, tt.args.end)
  7374  			if gotStart != tt.wantStart {
  7375  				t.Errorf("alignPCs() got start = %v, want %v", gotStart, tt.wantStart)
  7376  			}
  7377  			if gotEnd != tt.wantEnd {
  7378  				t.Errorf("alignPCs() got end = %v, want %v", gotEnd, tt.wantEnd)
  7379  			}
  7380  		})
  7381  	}
  7382  }
  7383  
  7384  func TestFindInstructions(t *testing.T) {
  7385  	numInstructions := 100
  7386  	startPC := 0x1000
  7387  	procInstructions := make([]proc.AsmInstruction, numInstructions)
  7388  	for i := 0; i < len(procInstructions); i++ {
  7389  		procInstructions[i] = proc.AsmInstruction{
  7390  			Loc: proc.Location{
  7391  				PC: uint64(startPC + 2*i),
  7392  			},
  7393  		}
  7394  	}
  7395  	type args struct {
  7396  		addr   uint64
  7397  		offset int
  7398  		count  int
  7399  	}
  7400  	tests := []struct {
  7401  		name             string
  7402  		args             args
  7403  		wantInstructions []proc.AsmInstruction
  7404  		wantOffset       int
  7405  		wantErr          bool
  7406  	}{
  7407  		{
  7408  			name: "request all",
  7409  			args: args{
  7410  				addr:   uint64(startPC),
  7411  				offset: 0,
  7412  				count:  100,
  7413  			},
  7414  			wantInstructions: procInstructions,
  7415  			wantOffset:       0,
  7416  			wantErr:          false,
  7417  		},
  7418  		{
  7419  			name: "request all (with offset)",
  7420  			args: args{
  7421  				addr:   uint64(startPC + numInstructions), // the instruction addr at numInstructions/2
  7422  				offset: -numInstructions / 2,
  7423  				count:  numInstructions,
  7424  			},
  7425  			wantInstructions: procInstructions,
  7426  			wantOffset:       0,
  7427  			wantErr:          false,
  7428  		},
  7429  		{
  7430  			name: "request half (with offset)",
  7431  			args: args{
  7432  				addr:   uint64(startPC),
  7433  				offset: 0,
  7434  				count:  numInstructions / 2,
  7435  			},
  7436  			wantInstructions: procInstructions[:numInstructions/2],
  7437  			wantOffset:       0,
  7438  			wantErr:          false,
  7439  		},
  7440  		{
  7441  			name: "request half (with offset)",
  7442  			args: args{
  7443  				addr:   uint64(startPC),
  7444  				offset: numInstructions / 2,
  7445  				count:  numInstructions / 2,
  7446  			},
  7447  			wantInstructions: procInstructions[numInstructions/2:],
  7448  			wantOffset:       0,
  7449  			wantErr:          false,
  7450  		},
  7451  		{
  7452  			name: "request too many",
  7453  			args: args{
  7454  				addr:   uint64(startPC),
  7455  				offset: 0,
  7456  				count:  numInstructions * 2,
  7457  			},
  7458  			wantInstructions: procInstructions,
  7459  			wantOffset:       0,
  7460  			wantErr:          false,
  7461  		},
  7462  		{
  7463  			name: "request too many with offset",
  7464  			args: args{
  7465  				addr:   uint64(startPC),
  7466  				offset: -numInstructions,
  7467  				count:  numInstructions * 2,
  7468  			},
  7469  			wantInstructions: procInstructions,
  7470  			wantOffset:       numInstructions,
  7471  			wantErr:          false,
  7472  		},
  7473  		{
  7474  			name: "request out of bounds",
  7475  			args: args{
  7476  				addr:   uint64(startPC),
  7477  				offset: -numInstructions,
  7478  				count:  numInstructions,
  7479  			},
  7480  			wantInstructions: []proc.AsmInstruction{},
  7481  			wantOffset:       0,
  7482  			wantErr:          false,
  7483  		},
  7484  		{
  7485  			name: "request out of bounds",
  7486  			args: args{
  7487  				addr:   uint64(startPC + 2*(numInstructions-1)),
  7488  				offset: 1,
  7489  				count:  numInstructions,
  7490  			},
  7491  			wantInstructions: []proc.AsmInstruction{},
  7492  			wantOffset:       0,
  7493  			wantErr:          false,
  7494  		},
  7495  		{
  7496  			name: "addr out of bounds (low)",
  7497  			args: args{
  7498  				addr:   0,
  7499  				offset: 0,
  7500  				count:  100,
  7501  			},
  7502  			wantInstructions: nil,
  7503  			wantOffset:       -1,
  7504  			wantErr:          true,
  7505  		},
  7506  		{
  7507  			name: "addr out of bounds (high)",
  7508  			args: args{
  7509  				addr:   uint64(startPC + 2*(numInstructions+1)),
  7510  				offset: -10,
  7511  				count:  20,
  7512  			},
  7513  			wantInstructions: nil,
  7514  			wantOffset:       -1,
  7515  			wantErr:          true,
  7516  		},
  7517  		{
  7518  			name: "addr not aligned",
  7519  			args: args{
  7520  				addr:   uint64(startPC + 1),
  7521  				offset: 0,
  7522  				count:  20,
  7523  			},
  7524  			wantInstructions: nil,
  7525  			wantOffset:       -1,
  7526  			wantErr:          true,
  7527  		},
  7528  	}
  7529  	for _, tt := range tests {
  7530  		t.Run(tt.name, func(t *testing.T) {
  7531  			gotInstructions, gotOffset, err := findInstructions(procInstructions, tt.args.addr, tt.args.offset, tt.args.count)
  7532  			if (err != nil) != tt.wantErr {
  7533  				t.Errorf("findInstructions() error = %v, wantErr %v", err, tt.wantErr)
  7534  				return
  7535  			}
  7536  			if !reflect.DeepEqual(gotInstructions, tt.wantInstructions) {
  7537  				t.Errorf("findInstructions() got instructions = %v, want %v", gotInstructions, tt.wantInstructions)
  7538  			}
  7539  			if gotOffset != tt.wantOffset {
  7540  				t.Errorf("findInstructions() got offset = %v, want %v", gotOffset, tt.wantOffset)
  7541  			}
  7542  		})
  7543  	}
  7544  }
  7545  
  7546  func TestDisassembleCgo(t *testing.T) {
  7547  	// Test that disassembling a program containing cgo code does not create problems.
  7548  	// See issue #3040
  7549  	protest.MustHaveCgo(t)
  7550  	runTestBuildFlags(t, "cgodisass", func(client *daptest.Client, fixture protest.Fixture) {
  7551  		runDebugSessionWithBPs(t, client, "launch",
  7552  			// Launch
  7553  			func() {
  7554  				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
  7555  			},
  7556  			// Set breakpoints
  7557  			fixture.Source, []int{11},
  7558  			[]onBreakpoint{{
  7559  				execute: func() {
  7560  					checkStop(t, client, 1, "main.main", 11)
  7561  
  7562  					client.StackTraceRequest(1, 0, 1)
  7563  					st := client.ExpectStackTraceResponse(t)
  7564  					if len(st.Body.StackFrames) < 1 {
  7565  						t.Fatalf("\ngot  %#v\nwant len(stackframes) => 1", st)
  7566  					}
  7567  
  7568  					// Request the single instruction that the program is stopped at.
  7569  					pc := st.Body.StackFrames[0].InstructionPointerReference
  7570  
  7571  					client.DisassembleRequest(pc, -200, 400)
  7572  					client.ExpectDisassembleResponse(t)
  7573  				},
  7574  				disconnect: true,
  7575  			}},
  7576  		)
  7577  	},
  7578  		protest.AllNonOptimized, true)
  7579  }
  7580  
  7581  func TestRedirect(t *testing.T) {
  7582  	runTest(t, "out_redirect", func(client *daptest.Client, fixture protest.Fixture) {
  7583  		// 1 >> initialize, << initialize
  7584  		client.InitializeRequest()
  7585  		initResp := client.ExpectInitializeResponseAndCapabilities(t)
  7586  		if initResp.Seq != 0 || initResp.RequestSeq != 1 {
  7587  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=1", initResp)
  7588  		}
  7589  
  7590  		// 2 >> launch, << initialized, << launch
  7591  		client.LaunchRequestWithArgs(map[string]interface{}{
  7592  			"request":    "launch",
  7593  			"mode":       "debug",
  7594  			"program":    fixture.Source,
  7595  			"outputMode": "remote",
  7596  		})
  7597  		initEvent := client.ExpectInitializedEvent(t)
  7598  		if initEvent.Seq != 0 {
  7599  			t.Errorf("\ngot %#v\nwant Seq=0", initEvent)
  7600  		}
  7601  		launchResp := client.ExpectLaunchResponse(t)
  7602  		if launchResp.Seq != 0 || launchResp.RequestSeq != 2 {
  7603  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=2", launchResp)
  7604  		}
  7605  
  7606  		// 5 >> configurationDone, << stopped, << configurationDone
  7607  		client.ConfigurationDoneRequest()
  7608  
  7609  		cdResp := client.ExpectConfigurationDoneResponse(t)
  7610  		if cdResp.Seq != 0 || cdResp.RequestSeq != 3 {
  7611  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=5", cdResp)
  7612  		}
  7613  
  7614  		// 6 << output, << terminated
  7615  		var (
  7616  			stdout = bytes.NewBufferString("")
  7617  			stderr = bytes.NewBufferString("")
  7618  		)
  7619  
  7620  	terminatedPoint:
  7621  		for {
  7622  			message := client.ExpectMessage(t)
  7623  			switch m := message.(type) {
  7624  			case *dap.OutputEvent:
  7625  				switch m.Body.Category {
  7626  				case "stdout":
  7627  					stdout.WriteString(m.Body.Output)
  7628  				case "stderr":
  7629  					stderr.WriteString(m.Body.Output)
  7630  				default:
  7631  					t.Errorf("\ngot %#v\nwant Category='stdout' or 'stderr'", m)
  7632  				}
  7633  			case *dap.TerminatedEvent:
  7634  				break terminatedPoint
  7635  			default:
  7636  				t.Errorf("\n got %#v, want *dap.OutputEvent or *dap.TerminateResponse", m)
  7637  			}
  7638  		}
  7639  
  7640  		var (
  7641  			expectStdout = "hello world!\nhello world!"
  7642  			expectStderr = "hello world!\nhello world! error!"
  7643  		)
  7644  
  7645  		// check output
  7646  		if expectStdout != stdout.String() {
  7647  			t.Errorf("\n got stdout: len:%d\n%s\nwant: len:%d\n%s", stdout.Len(), stdout.String(), len(expectStdout), expectStdout)
  7648  		}
  7649  
  7650  		if expectStderr != stderr.String() {
  7651  			t.Errorf("\n got stderr: len:%d \n%s\nwant: len:%d\n%s", stderr.Len(), stderr.String(), len(expectStderr), expectStderr)
  7652  		}
  7653  
  7654  		// 7 >> disconnect, << disconnect
  7655  		client.DisconnectRequest()
  7656  		oep := client.ExpectOutputEventProcessExited(t, 0)
  7657  		if oep.Seq != 0 || oep.Body.Category != "console" {
  7658  			t.Errorf("\ngot %#v\nwant Seq=0 Category='console'", oep)
  7659  		}
  7660  		oed := client.ExpectOutputEventDetaching(t)
  7661  		if oed.Seq != 0 || oed.Body.Category != "console" {
  7662  			t.Errorf("\ngot %#v\nwant Seq=0 Category='console'", oed)
  7663  		}
  7664  		dResp := client.ExpectDisconnectResponse(t)
  7665  		if dResp.Seq != 0 || dResp.RequestSeq != 4 {
  7666  			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=43", dResp)
  7667  		}
  7668  		client.ExpectTerminatedEvent(t)
  7669  	})
  7670  }
  7671  
  7672  // Helper functions for checking ErrorMessage field values.
  7673  
  7674  func checkErrorMessageId(er *dap.ErrorMessage, id int) bool {
  7675  	return er != nil && er.Id == id
  7676  }
  7677  
  7678  func checkErrorMessageFormat(er *dap.ErrorMessage, fmt string) bool {
  7679  	return er != nil && er.Format == fmt
  7680  }