diff --git a/service/dap/daptest/client.go b/service/dap/daptest/client.go index e2390c8f..61d6c687 100644 --- a/service/dap/daptest/client.go +++ b/service/dap/daptest/client.go @@ -346,6 +346,13 @@ func (c *Client) InitializeRequest() { c.send(request) } +// InitializeRequestWithArgs sends an 'initialize' request with specified arguments. +func (c *Client) InitializeRequestWithArgs(args dap.InitializeRequestArguments) { + request := &dap.InitializeRequest{Request: *c.newRequest("initialize")} + request.Arguments = args + c.send(request) +} + // LaunchRequest sends a 'launch' request with the specified args. func (c *Client) LaunchRequest(mode, program string, stopOnEntry bool) { request := &dap.LaunchRequest{Request: *c.newRequest("launch")} diff --git a/service/dap/error_ids.go b/service/dap/error_ids.go index 68a4d3de..9cab0bcb 100644 --- a/service/dap/error_ids.go +++ b/service/dap/error_ids.go @@ -12,6 +12,7 @@ const ( // values below are inspired the original vscode-go debug adaptor. FailedToLaunch = 3000 FailedToAttach = 3001 + FailedToInitialize = 3002 UnableToSetBreakpoints = 2002 UnableToDisplayThreads = 2003 UnableToProduceStackTrace = 2004 diff --git a/service/dap/server.go b/service/dap/server.go index d786a35a..3a15538d 100644 --- a/service/dap/server.go +++ b/service/dap/server.go @@ -608,6 +608,22 @@ func (s *Server) logToConsole(msg string) { } func (s *Server) onInitializeRequest(request *dap.InitializeRequest) { + if request.Arguments.PathFormat != "path" { + s.sendErrorResponse(request.Request, FailedToInitialize, "Failed to initialize", + fmt.Sprintf("Unsupported 'pathFormat' value '%s'.", request.Arguments.PathFormat)) + return + } + if !request.Arguments.LinesStartAt1 { + s.sendErrorResponse(request.Request, FailedToInitialize, "Failed to initialize", + "Only 1-based line numbers are supported.") + return + } + if !request.Arguments.ColumnsStartAt1 { + s.sendErrorResponse(request.Request, FailedToInitialize, "Failed to initialize", + "Only 1-based column numbers are supported.") + return + } + // TODO(polina): Respond with an error if debug session is in progress? response := &dap.InitializeResponse{Response: *newResponse(request.Request)} response.Body.SupportsConfigurationDoneRequest = true diff --git a/service/dap/server_test.go b/service/dap/server_test.go index 65e011ad..7504b8da 100644 --- a/service/dap/server_test.go +++ b/service/dap/server_test.go @@ -50,6 +50,16 @@ func runTest(t *testing.T, name string, test func(c *daptest.Client, f protest.F var buildFlags protest.BuildFlags = protest.AllNonOptimized fixture := protest.BuildFixture(name, buildFlags) + // Start the DAP server. + client := startDapServer(t) + // client.Close will close the client connectinon, which will cause a connection error + // on the server side and signal disconnect to unblock Stop() above. + defer client.Close() + + test(client, fixture) +} + +func startDapServer(t *testing.T) *daptest.Client { // Start the DAP server. listener, err := net.Listen("tcp", ":0") if err != nil { @@ -76,11 +86,7 @@ func runTest(t *testing.T, name string, test func(c *daptest.Client, f protest.F }() client := daptest.NewClient(listener.Addr().String()) - // This will close the client connectinon, which will cause a connection error - // on the server side and signal disconnect to unblock Stop() above. - defer client.Close() - - test(client, fixture) + return client } // TestLaunchStopOnEntry emulates the message exchange that can be observed with @@ -3337,6 +3343,66 @@ func TestBadAttachRequest(t *testing.T) { }) } +func TestBadInitializeRequest(t *testing.T) { + runInitializeTest := func(args dap.InitializeRequestArguments, err string) { + t.Helper() + // Only one initialize request is allowed, so use a new server + // for each test. + client := startDapServer(t) + // client.Close will close the client connectinon, which will cause a connection error + // on the server side and signal disconnect to unblock Stop() above. + defer client.Close() + + client.InitializeRequestWithArgs(args) + response := client.ExpectErrorResponse(t) + if response.Command != "initialize" { + t.Errorf("Command got %q, want \"launch\"", response.Command) + } + if response.Message != "Failed to initialize" { + t.Errorf("Message got %q, want \"Failed to launch\"", response.Message) + } + if response.Body.Error.Id != 3002 { + t.Errorf("Id got %d, want 3002", response.Body.Error.Id) + } + if response.Body.Error.Format != err { + t.Errorf("\ngot %q\nwant %q", response.Body.Error.Format, err) + } + } + + // Bad path format. + runInitializeTest(dap.InitializeRequestArguments{ + AdapterID: "go", + PathFormat: "url", // unsupported 'pathFormat' + LinesStartAt1: true, + ColumnsStartAt1: true, + Locale: "en-us", + }, + "Failed to initialize: Unsupported 'pathFormat' value 'url'.", + ) + + // LinesStartAt1 must be true. + runInitializeTest(dap.InitializeRequestArguments{ + AdapterID: "go", + PathFormat: "path", + LinesStartAt1: false, // only 1-based line numbers are supported + ColumnsStartAt1: true, + Locale: "en-us", + }, + "Failed to initialize: Only 1-based line numbers are supported.", + ) + + // ColumnsStartAt1 must be true. + runInitializeTest(dap.InitializeRequestArguments{ + AdapterID: "go", + PathFormat: "path", + LinesStartAt1: true, + ColumnsStartAt1: false, // only 1-based column numbers are supported + Locale: "en-us", + }, + "Failed to initialize: Only 1-based column numbers are supported.", + ) +} + func TestBadlyFormattedMessageToServer(t *testing.T) { runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { // Send a badly formatted message to the server, and expect it to close the