Repository: go-delve/delve
Pull request #2441 (2021-04-19 02:22:20)

daptest/gen: autogenerate some assertion functions
daptest has type assertion functions that panic if the read response/event message is not
the expected type. This is not only against the recommended style guideline (Don't Panic, Useful
Test Failures, ...), but also it prevents from quickly diagnosing test failures occurred in remote CIs.

This PR changes the type assertion to the two return value type assertion, and t.Fatal with details
if the type is not expected. `service/dap/daptest/main.go` is a program that auto-generates those assertion functions in resp.go.

Run `go generate` from the service/dap directory to update resp.go.

suzmue: LGTM.

The test failure is from service/dap/daptest/client.go:36 "Audit the logger for user defined data that could break log file pattern", which isn't part of the code that you changed.
derekparker: @polinasok any other thoughts here before merge?
derekparker: Just needs rebase.
hyangah: Done.

Patch:
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


Test patch:
diff --git a/service/dap/daptest/client.go b/service/dap/daptest/client.go
index e2390c8f..abc3a11f 100644
--- a/service/dap/daptest/client.go
+++ b/service/dap/daptest/client.go
@@ -2,12 +2,16 @@
 // for DAP mode testing.
 package daptest
 
+//go:generate go run ./gen/main.go -o ./resp.go
+
 import (
 	"bufio"
+	"encoding/json"
 	"fmt"
 	"log"
 	"net"
 	"path/filepath"
+	"reflect"
 	"regexp"
 	"testing"
 
@@ -60,9 +64,9 @@ func (c *Client) ExpectMessage(t *testing.T) dap.Message {
 	return m
 }
 
-func (c *Client) ExpectErrorResponse(t *testing.T) *dap.ErrorResponse {
+func (c *Client) ExpectInvisibleErrorResponse(t *testing.T) *dap.ErrorResponse {
 	t.Helper()
-	er := c.ExpectMessage(t).(*dap.ErrorResponse)
+	er := c.ExpectErrorResponse(t)
 	if er.Body.Error.ShowUser {
 		t.Errorf("\ngot %#v\nwant ShowUser=false", er)
 	}
@@ -71,7 +75,7 @@ func (c *Client) ExpectErrorResponse(t *testing.T) *dap.ErrorResponse {
 
 func (c *Client) ExpectVisibleErrorResponse(t *testing.T) *dap.ErrorResponse {
 	t.Helper()
-	er := c.ExpectMessage(t).(*dap.ErrorResponse)
+	er := c.ExpectErrorResponse(t)
 	if !er.Body.Error.ShowUser {
 		t.Errorf("\ngot %#v\nwant ShowUser=true", er)
 	}
@@ -80,100 +84,47 @@ func (c *Client) ExpectVisibleErrorResponse(t *testing.T) *dap.ErrorResponse {
 
 func (c *Client) expectErrorResponse(t *testing.T, id int, message string) *dap.ErrorResponse {
 	t.Helper()
-	er := c.ExpectMessage(t).(*dap.ErrorResponse)
+	er := c.ExpectErrorResponse(t)
 	if er.Body.Error.Id != id || er.Message != message {
 		t.Errorf("\ngot %#v\nwant Id=%d Message=%q", er, id, message)
 	}
 	return er
 }
 
-func (c *Client) ExpectNotYetImplementedErrorResponse(t *testing.T) *dap.ErrorResponse {
-	t.Helper()
-	return c.expectErrorResponse(t, 7777, "Not yet implemented")
-}
-
-func (c *Client) ExpectUnsupportedCommandErrorResponse(t *testing.T) *dap.ErrorResponse {
-	t.Helper()
-	return c.expectErrorResponse(t, 9999, "Unsupported command")
-}
-
-func (c *Client) ExpectDisconnectResponse(t *testing.T) *dap.DisconnectResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.DisconnectResponse)
-}
-
-func (c *Client) ExpectContinueResponse(t *testing.T) *dap.ContinueResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.ContinueResponse)
-}
-
-func (c *Client) ExpectNextResponse(t *testing.T) *dap.NextResponse {
+func (c *Client) ExpectInitializeResponseAndCapabilities(t *testing.T) *dap.InitializeResponse {
 	t.Helper()
-	return c.ExpectMessage(t).(*dap.NextResponse)
-}
-
-func (c *Client) ExpectStepInResponse(t *testing.T) *dap.StepInResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.StepInResponse)
-}
-
-func (c *Client) ExpectStepOutResponse(t *testing.T) *dap.StepOutResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.StepOutResponse)
-}
-
-func (c *Client) ExpectTerminatedEvent(t *testing.T) *dap.TerminatedEvent {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.TerminatedEvent)
-}
-
-func (c *Client) ExpectInitializeResponse(t *testing.T) *dap.InitializeResponse {
-	t.Helper()
-	initResp := c.ExpectMessage(t).(*dap.InitializeResponse)
-	if !initResp.Body.SupportsConfigurationDoneRequest {
-		t.Errorf("got %#v, want SupportsConfigurationDoneRequest=true", initResp)
+	initResp := c.ExpectInitializeResponse(t)
+	wantCapabilities := dap.Capabilities{
+		// the values set by dap.(*Server).onInitializeRequest.
+		SupportsConfigurationDoneRequest: true,
+		SupportsConditionalBreakpoints:   true,
+		SupportsDelayedStackTraceLoading: true,
+		SupportTerminateDebuggee:         true,
+	}
+	if !reflect.DeepEqual(initResp.Body, wantCapabilities) {
+		t.Errorf("capabilities in initializeResponse: got %+v, want %v", pretty(initResp.Body), pretty(wantCapabilities))
 	}
 	return initResp
 }
 
-func (c *Client) ExpectInitializedEvent(t *testing.T) *dap.InitializedEvent {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.InitializedEvent)
+func pretty(v interface{}) string {
+	s, _ := json.MarshalIndent(v, "", "\t")
+	return string(s)
 }
 
-func (c *Client) ExpectLaunchResponse(t *testing.T) *dap.LaunchResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.LaunchResponse)
-}
-
-func (c *Client) ExpectAttachResponse(t *testing.T) *dap.AttachResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.AttachResponse)
-}
-
-func (c *Client) ExpectSetExceptionBreakpointsResponse(t *testing.T) *dap.SetExceptionBreakpointsResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.SetExceptionBreakpointsResponse)
-}
-
-func (c *Client) ExpectSetBreakpointsResponse(t *testing.T) *dap.SetBreakpointsResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.SetBreakpointsResponse)
-}
-
-func (c *Client) ExpectStoppedEvent(t *testing.T) *dap.StoppedEvent {
+func (c *Client) ExpectNotYetImplementedErrorResponse(t *testing.T) *dap.ErrorResponse {
 	t.Helper()
-	return c.ExpectMessage(t).(*dap.StoppedEvent)
+	return c.expectErrorResponse(t, 7777, "Not yet implemented")
 }
 
-func (c *Client) ExpectOutputEvent(t *testing.T) *dap.OutputEvent {
+func (c *Client) ExpectUnsupportedCommandErrorResponse(t *testing.T) *dap.ErrorResponse {
 	t.Helper()
-	return c.ExpectMessage(t).(*dap.OutputEvent)
+	return c.expectErrorResponse(t, 9999, "Unsupported command")
 }
 
 func (c *Client) ExpectOutputEventRegex(t *testing.T, want string) *dap.OutputEvent {
 	t.Helper()
-	e := c.ExpectMessage(t).(*dap.OutputEvent)
+	e := c.ExpectOutputEvent(t)
 	if matched, _ := regexp.MatchString(want, e.Body.Output); !matched {
 		t.Errorf("\ngot %#v\nwant Output=%q", e, want)
 	}
@@ -200,136 +151,6 @@ func (c *Client) ExpectOutputEventDetachingNoKill(t *testing.T) *dap.OutputEvent
 	return c.ExpectOutputEventRegex(t, `Detaching without terminating target process\n`)
 }
 
-func (c *Client) ExpectConfigurationDoneResponse(t *testing.T) *dap.ConfigurationDoneResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.ConfigurationDoneResponse)
-}
-
-func (c *Client) ExpectThreadsResponse(t *testing.T) *dap.ThreadsResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.ThreadsResponse)
-}
-
-func (c *Client) ExpectStackTraceResponse(t *testing.T) *dap.StackTraceResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.StackTraceResponse)
-}
-
-func (c *Client) ExpectScopesResponse(t *testing.T) *dap.ScopesResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.ScopesResponse)
-}
-
-func (c *Client) ExpectVariablesResponse(t *testing.T) *dap.VariablesResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.VariablesResponse)
-}
-
-func (c *Client) ExpectEvaluateResponse(t *testing.T) *dap.EvaluateResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.EvaluateResponse)
-}
-
-func (c *Client) ExpectTerminateResponse(t *testing.T) *dap.TerminateResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.TerminateResponse)
-}
-
-func (c *Client) ExpectRestartResponse(t *testing.T) *dap.RestartResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.RestartResponse)
-}
-
-func (c *Client) ExpectSetFunctionBreakpointsResponse(t *testing.T) *dap.SetFunctionBreakpointsResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.SetFunctionBreakpointsResponse)
-}
-
-func (c *Client) ExpectStepBackResponse(t *testing.T) *dap.StepBackResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.StepBackResponse)
-}
-
-func (c *Client) ExpectReverseContinueResponse(t *testing.T) *dap.ReverseContinueResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.ReverseContinueResponse)
-}
-
-func (c *Client) ExpectRestartFrameResponse(t *testing.T) *dap.RestartFrameResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.RestartFrameResponse)
-}
-
-func (c *Client) ExpectSetExpressionResponse(t *testing.T) *dap.SetExpressionResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.SetExpressionResponse)
-}
-
-func (c *Client) ExpectTerminateThreadsResponse(t *testing.T) *dap.TerminateThreadsResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.TerminateThreadsResponse)
-}
-
-func (c *Client) ExpectStepInTargetsResponse(t *testing.T) *dap.StepInTargetsResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.StepInTargetsResponse)
-}
-
-func (c *Client) ExpectGotoTargetsResponse(t *testing.T) *dap.GotoTargetsResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.GotoTargetsResponse)
-}
-
-func (c *Client) ExpectCompletionsResponse(t *testing.T) *dap.CompletionsResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.CompletionsResponse)
-}
-
-func (c *Client) ExpectExceptionInfoResponse(t *testing.T) *dap.ExceptionInfoResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.ExceptionInfoResponse)
-}
-
-func (c *Client) ExpectLoadedSourcesResponse(t *testing.T) *dap.LoadedSourcesResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.LoadedSourcesResponse)
-}
-
-func (c *Client) ExpectDataBreakpointInfoResponse(t *testing.T) *dap.DataBreakpointInfoResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.DataBreakpointInfoResponse)
-}
-
-func (c *Client) ExpectSetDataBreakpointsResponse(t *testing.T) *dap.SetDataBreakpointsResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.SetDataBreakpointsResponse)
-}
-
-func (c *Client) ExpectReadMemoryResponse(t *testing.T) *dap.ReadMemoryResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.ReadMemoryResponse)
-}
-
-func (c *Client) ExpectDisassembleResponse(t *testing.T) *dap.DisassembleResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.DisassembleResponse)
-}
-
-func (c *Client) ExpectCancelResponse(t *testing.T) *dap.CancelResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.CancelResponse)
-}
-
-func (c *Client) ExpectBreakpointLocationsResponse(t *testing.T) *dap.BreakpointLocationsResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.BreakpointLocationsResponse)
-}
-
-func (c *Client) ExpectModulesResponse(t *testing.T) *dap.ModulesResponse {
-	t.Helper()
-	return c.ExpectMessage(t).(*dap.ModulesResponse)
-}
-
 // InitializeRequest sends an 'initialize' request.
 func (c *Client) InitializeRequest() {
 	request := &dap.InitializeRequest{Request: *c.newRequest("initialize")}
@@ -346,6 +167,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/daptest/gen/main.go b/service/dap/daptest/gen/main.go
new file mode 100644
index 00000000..41aa002a
--- /dev/null
+++ b/service/dap/daptest/gen/main.go
@@ -0,0 +1,99 @@
+// Binary gen generates service/dap/daptest/responses.go.
+package main
+
+import (
+	"bytes"
+	"flag"
+	"fmt"
+	"go/format"
+	"go/types"
+	"io/ioutil"
+	"os"
+	"text/template"
+
+	"golang.org/x/tools/go/packages"
+
+	_ "github.com/google/go-dap"
+)
+
+var oFlag = flag.String("o", "", "output file name")
+
+var tmpl = template.Must(template.New("assert").Parse(`package daptest
+
+// Code generated by go generate; DO NOT EDIT.
+// The code generator program is in ./gen directory.
+
+import (
+	"testing"
+
+	"github.com/google/go-dap"
+)
+{{range .}}
+// Expect{{.}} reads a protocol message from the connection
+// and fails the test if the read message is not *{{.}}.
+func (c *Client) Expect{{.}}(t *testing.T) *dap.{{.}} {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.{{.}})
+	if !ok {
+		t.Fatalf("got %q, want *dap.{{.}}", m)
+	}
+	return r
+}{{end}}
+`))
+
+func main() {
+	flag.Parse()
+
+	if *oFlag == "" {
+		fmt.Fprintf(os.Stderr, "-o must be provided\n")
+	}
+
+	pkgs, err := packages.Load(&packages.Config{
+		Mode: packages.NeedTypes,
+	}, "github.com/google/go-dap")
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "load: %v\n", err)
+		os.Exit(1)
+	}
+	if len(pkgs) != 1 || pkgs[0].Types == nil {
+		fmt.Fprintf(os.Stderr, "invalid package was loaded: %#v\n", pkgs)
+		os.Exit(1)
+	}
+
+	messages := []string{}
+	scope := pkgs[0].Types.Scope()
+	for _, name := range scope.Names() {
+		// Find only types that are embedding go-dap.Respose message.
+		obj := scope.Lookup(name)
+		if !obj.Exported() {
+			continue // skip unexported
+		}
+		u, ok := obj.Type().Underlying().(*types.Struct)
+		if !ok {
+			continue
+		}
+		for i := 0; i < u.NumFields(); i++ {
+			if f := u.Field(i); f.Embedded() && (f.Type().String() == "github.com/google/go-dap.Response" ||
+				f.Type().String() == "github.com/google/go-dap.Event") {
+				messages = append(messages, obj.Name())
+				break
+			}
+		}
+	}
+
+	buf := &bytes.Buffer{}
+	if err := tmpl.Execute(buf, messages); err != nil {
+		fmt.Fprintf(os.Stderr, "Failed to generate: %v\n", err)
+		os.Exit(1)
+	}
+	formatted, err := format.Source(buf.Bytes())
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Generated invalid go code: %v\n", err)
+		os.Exit(1)
+	}
+	if err := ioutil.WriteFile(*oFlag, formatted, 0644); err != nil {
+		fmt.Fprintf(os.Stderr, "Failed to write: %v\n", err)
+		os.Exit(1)
+	}
+}
diff --git a/service/dap/daptest/resp.go b/service/dap/daptest/resp.go
new file mode 100644
index 00000000..086455ba
--- /dev/null
+++ b/service/dap/daptest/resp.go
@@ -0,0 +1,718 @@
+package daptest
+
+// Code generated by go generate; DO NOT EDIT.
+// The code generator program is in ./gen directory.
+
+import (
+	"testing"
+
+	"github.com/google/go-dap"
+)
+
+// ExpectAttachResponse reads a protocol message from the connection
+// and fails the test if the read message is not *AttachResponse.
+func (c *Client) ExpectAttachResponse(t *testing.T) *dap.AttachResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.AttachResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.AttachResponse", m)
+	}
+	return r
+}
+
+// ExpectBreakpointEvent reads a protocol message from the connection
+// and fails the test if the read message is not *BreakpointEvent.
+func (c *Client) ExpectBreakpointEvent(t *testing.T) *dap.BreakpointEvent {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.BreakpointEvent)
+	if !ok {
+		t.Fatalf("got %q, want *dap.BreakpointEvent", m)
+	}
+	return r
+}
+
+// ExpectBreakpointLocationsResponse reads a protocol message from the connection
+// and fails the test if the read message is not *BreakpointLocationsResponse.
+func (c *Client) ExpectBreakpointLocationsResponse(t *testing.T) *dap.BreakpointLocationsResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.BreakpointLocationsResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.BreakpointLocationsResponse", m)
+	}
+	return r
+}
+
+// ExpectCancelResponse reads a protocol message from the connection
+// and fails the test if the read message is not *CancelResponse.
+func (c *Client) ExpectCancelResponse(t *testing.T) *dap.CancelResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.CancelResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.CancelResponse", m)
+	}
+	return r
+}
+
+// ExpectCapabilitiesEvent reads a protocol message from the connection
+// and fails the test if the read message is not *CapabilitiesEvent.
+func (c *Client) ExpectCapabilitiesEvent(t *testing.T) *dap.CapabilitiesEvent {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.CapabilitiesEvent)
+	if !ok {
+		t.Fatalf("got %q, want *dap.CapabilitiesEvent", m)
+	}
+	return r
+}
+
+// ExpectCompletionsResponse reads a protocol message from the connection
+// and fails the test if the read message is not *CompletionsResponse.
+func (c *Client) ExpectCompletionsResponse(t *testing.T) *dap.CompletionsResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.CompletionsResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.CompletionsResponse", m)
+	}
+	return r
+}
+
+// ExpectConfigurationDoneResponse reads a protocol message from the connection
+// and fails the test if the read message is not *ConfigurationDoneResponse.
+func (c *Client) ExpectConfigurationDoneResponse(t *testing.T) *dap.ConfigurationDoneResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.ConfigurationDoneResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.ConfigurationDoneResponse", m)
+	}
+	return r
+}
+
+// ExpectContinueResponse reads a protocol message from the connection
+// and fails the test if the read message is not *ContinueResponse.
+func (c *Client) ExpectContinueResponse(t *testing.T) *dap.ContinueResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.ContinueResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.ContinueResponse", m)
+	}
+	return r
+}
+
+// ExpectContinuedEvent reads a protocol message from the connection
+// and fails the test if the read message is not *ContinuedEvent.
+func (c *Client) ExpectContinuedEvent(t *testing.T) *dap.ContinuedEvent {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.ContinuedEvent)
+	if !ok {
+		t.Fatalf("got %q, want *dap.ContinuedEvent", m)
+	}
+	return r
+}
+
+// ExpectDataBreakpointInfoResponse reads a protocol message from the connection
+// and fails the test if the read message is not *DataBreakpointInfoResponse.
+func (c *Client) ExpectDataBreakpointInfoResponse(t *testing.T) *dap.DataBreakpointInfoResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.DataBreakpointInfoResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.DataBreakpointInfoResponse", m)
+	}
+	return r
+}
+
+// ExpectDisassembleResponse reads a protocol message from the connection
+// and fails the test if the read message is not *DisassembleResponse.
+func (c *Client) ExpectDisassembleResponse(t *testing.T) *dap.DisassembleResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.DisassembleResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.DisassembleResponse", m)
+	}
+	return r
+}
+
+// ExpectDisconnectResponse reads a protocol message from the connection
+// and fails the test if the read message is not *DisconnectResponse.
+func (c *Client) ExpectDisconnectResponse(t *testing.T) *dap.DisconnectResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.DisconnectResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.DisconnectResponse", m)
+	}
+	return r
+}
+
+// ExpectErrorResponse reads a protocol message from the connection
+// and fails the test if the read message is not *ErrorResponse.
+func (c *Client) ExpectErrorResponse(t *testing.T) *dap.ErrorResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.ErrorResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.ErrorResponse", m)
+	}
+	return r
+}
+
+// ExpectEvaluateResponse reads a protocol message from the connection
+// and fails the test if the read message is not *EvaluateResponse.
+func (c *Client) ExpectEvaluateResponse(t *testing.T) *dap.EvaluateResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.EvaluateResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.EvaluateResponse", m)
+	}
+	return r
+}
+
+// ExpectExceptionInfoResponse reads a protocol message from the connection
+// and fails the test if the read message is not *ExceptionInfoResponse.
+func (c *Client) ExpectExceptionInfoResponse(t *testing.T) *dap.ExceptionInfoResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.ExceptionInfoResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.ExceptionInfoResponse", m)
+	}
+	return r
+}
+
+// ExpectExitedEvent reads a protocol message from the connection
+// and fails the test if the read message is not *ExitedEvent.
+func (c *Client) ExpectExitedEvent(t *testing.T) *dap.ExitedEvent {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.ExitedEvent)
+	if !ok {
+		t.Fatalf("got %q, want *dap.ExitedEvent", m)
+	}
+	return r
+}
+
+// ExpectGotoResponse reads a protocol message from the connection
+// and fails the test if the read message is not *GotoResponse.
+func (c *Client) ExpectGotoResponse(t *testing.T) *dap.GotoResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.GotoResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.GotoResponse", m)
+	}
+	return r
+}
+
+// ExpectGotoTargetsResponse reads a protocol message from the connection
+// and fails the test if the read message is not *GotoTargetsResponse.
+func (c *Client) ExpectGotoTargetsResponse(t *testing.T) *dap.GotoTargetsResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.GotoTargetsResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.GotoTargetsResponse", m)
+	}
+	return r
+}
+
+// ExpectInitializeResponse reads a protocol message from the connection
+// and fails the test if the read message is not *InitializeResponse.
+func (c *Client) ExpectInitializeResponse(t *testing.T) *dap.InitializeResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.InitializeResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.InitializeResponse", m)
+	}
+	return r
+}
+
+// ExpectInitializedEvent reads a protocol message from the connection
+// and fails the test if the read message is not *InitializedEvent.
+func (c *Client) ExpectInitializedEvent(t *testing.T) *dap.InitializedEvent {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.InitializedEvent)
+	if !ok {
+		t.Fatalf("got %q, want *dap.InitializedEvent", m)
+	}
+	return r
+}
+
+// ExpectInvalidatedEvent reads a protocol message from the connection
+// and fails the test if the read message is not *InvalidatedEvent.
+func (c *Client) ExpectInvalidatedEvent(t *testing.T) *dap.InvalidatedEvent {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.InvalidatedEvent)
+	if !ok {
+		t.Fatalf("got %q, want *dap.InvalidatedEvent", m)
+	}
+	return r
+}
+
+// ExpectLaunchResponse reads a protocol message from the connection
+// and fails the test if the read message is not *LaunchResponse.
+func (c *Client) ExpectLaunchResponse(t *testing.T) *dap.LaunchResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.LaunchResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.LaunchResponse", m)
+	}
+	return r
+}
+
+// ExpectLoadedSourceEvent reads a protocol message from the connection
+// and fails the test if the read message is not *LoadedSourceEvent.
+func (c *Client) ExpectLoadedSourceEvent(t *testing.T) *dap.LoadedSourceEvent {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.LoadedSourceEvent)
+	if !ok {
+		t.Fatalf("got %q, want *dap.LoadedSourceEvent", m)
+	}
+	return r
+}
+
+// ExpectLoadedSourcesResponse reads a protocol message from the connection
+// and fails the test if the read message is not *LoadedSourcesResponse.
+func (c *Client) ExpectLoadedSourcesResponse(t *testing.T) *dap.LoadedSourcesResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.LoadedSourcesResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.LoadedSourcesResponse", m)
+	}
+	return r
+}
+
+// ExpectModuleEvent reads a protocol message from the connection
+// and fails the test if the read message is not *ModuleEvent.
+func (c *Client) ExpectModuleEvent(t *testing.T) *dap.ModuleEvent {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.ModuleEvent)
+	if !ok {
+		t.Fatalf("got %q, want *dap.ModuleEvent", m)
+	}
+	return r
+}
+
+// ExpectModulesResponse reads a protocol message from the connection
+// and fails the test if the read message is not *ModulesResponse.
+func (c *Client) ExpectModulesResponse(t *testing.T) *dap.ModulesResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.ModulesResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.ModulesResponse", m)
+	}
+	return r
+}
+
+// ExpectNextResponse reads a protocol message from the connection
+// and fails the test if the read message is not *NextResponse.
+func (c *Client) ExpectNextResponse(t *testing.T) *dap.NextResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.NextResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.NextResponse", m)
+	}
+	return r
+}
+
+// ExpectOutputEvent reads a protocol message from the connection
+// and fails the test if the read message is not *OutputEvent.
+func (c *Client) ExpectOutputEvent(t *testing.T) *dap.OutputEvent {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.OutputEvent)
+	if !ok {
+		t.Fatalf("got %q, want *dap.OutputEvent", m)
+	}
+	return r
+}
+
+// ExpectPauseResponse reads a protocol message from the connection
+// and fails the test if the read message is not *PauseResponse.
+func (c *Client) ExpectPauseResponse(t *testing.T) *dap.PauseResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.PauseResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.PauseResponse", m)
+	}
+	return r
+}
+
+// ExpectProcessEvent reads a protocol message from the connection
+// and fails the test if the read message is not *ProcessEvent.
+func (c *Client) ExpectProcessEvent(t *testing.T) *dap.ProcessEvent {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.ProcessEvent)
+	if !ok {
+		t.Fatalf("got %q, want *dap.ProcessEvent", m)
+	}
+	return r
+}
+
+// ExpectProgressEndEvent reads a protocol message from the connection
+// and fails the test if the read message is not *ProgressEndEvent.
+func (c *Client) ExpectProgressEndEvent(t *testing.T) *dap.ProgressEndEvent {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.ProgressEndEvent)
+	if !ok {
+		t.Fatalf("got %q, want *dap.ProgressEndEvent", m)
+	}
+	return r
+}
+
+// ExpectProgressStartEvent reads a protocol message from the connection
+// and fails the test if the read message is not *ProgressStartEvent.
+func (c *Client) ExpectProgressStartEvent(t *testing.T) *dap.ProgressStartEvent {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.ProgressStartEvent)
+	if !ok {
+		t.Fatalf("got %q, want *dap.ProgressStartEvent", m)
+	}
+	return r
+}
+
+// ExpectProgressUpdateEvent reads a protocol message from the connection
+// and fails the test if the read message is not *ProgressUpdateEvent.
+func (c *Client) ExpectProgressUpdateEvent(t *testing.T) *dap.ProgressUpdateEvent {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.ProgressUpdateEvent)
+	if !ok {
+		t.Fatalf("got %q, want *dap.ProgressUpdateEvent", m)
+	}
+	return r
+}
+
+// ExpectReadMemoryResponse reads a protocol message from the connection
+// and fails the test if the read message is not *ReadMemoryResponse.
+func (c *Client) ExpectReadMemoryResponse(t *testing.T) *dap.ReadMemoryResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.ReadMemoryResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.ReadMemoryResponse", m)
+	}
+	return r
+}
+
+// ExpectRestartFrameResponse reads a protocol message from the connection
+// and fails the test if the read message is not *RestartFrameResponse.
+func (c *Client) ExpectRestartFrameResponse(t *testing.T) *dap.RestartFrameResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.RestartFrameResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.RestartFrameResponse", m)
+	}
+	return r
+}
+
+// ExpectRestartResponse reads a protocol message from the connection
+// and fails the test if the read message is not *RestartResponse.
+func (c *Client) ExpectRestartResponse(t *testing.T) *dap.RestartResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.RestartResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.RestartResponse", m)
+	}
+	return r
+}
+
+// ExpectReverseContinueResponse reads a protocol message from the connection
+// and fails the test if the read message is not *ReverseContinueResponse.
+func (c *Client) ExpectReverseContinueResponse(t *testing.T) *dap.ReverseContinueResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.ReverseContinueResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.ReverseContinueResponse", m)
+	}
+	return r
+}
+
+// ExpectRunInTerminalResponse reads a protocol message from the connection
+// and fails the test if the read message is not *RunInTerminalResponse.
+func (c *Client) ExpectRunInTerminalResponse(t *testing.T) *dap.RunInTerminalResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.RunInTerminalResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.RunInTerminalResponse", m)
+	}
+	return r
+}
+
+// ExpectScopesResponse reads a protocol message from the connection
+// and fails the test if the read message is not *ScopesResponse.
+func (c *Client) ExpectScopesResponse(t *testing.T) *dap.ScopesResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.ScopesResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.ScopesResponse", m)
+	}
+	return r
+}
+
+// ExpectSetBreakpointsResponse reads a protocol message from the connection
+// and fails the test if the read message is not *SetBreakpointsResponse.
+func (c *Client) ExpectSetBreakpointsResponse(t *testing.T) *dap.SetBreakpointsResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.SetBreakpointsResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.SetBreakpointsResponse", m)
+	}
+	return r
+}
+
+// ExpectSetDataBreakpointsResponse reads a protocol message from the connection
+// and fails the test if the read message is not *SetDataBreakpointsResponse.
+func (c *Client) ExpectSetDataBreakpointsResponse(t *testing.T) *dap.SetDataBreakpointsResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.SetDataBreakpointsResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.SetDataBreakpointsResponse", m)
+	}
+	return r
+}
+
+// ExpectSetExceptionBreakpointsResponse reads a protocol message from the connection
+// and fails the test if the read message is not *SetExceptionBreakpointsResponse.
+func (c *Client) ExpectSetExceptionBreakpointsResponse(t *testing.T) *dap.SetExceptionBreakpointsResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.SetExceptionBreakpointsResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.SetExceptionBreakpointsResponse", m)
+	}
+	return r
+}
+
+// ExpectSetExpressionResponse reads a protocol message from the connection
+// and fails the test if the read message is not *SetExpressionResponse.
+func (c *Client) ExpectSetExpressionResponse(t *testing.T) *dap.SetExpressionResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.SetExpressionResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.SetExpressionResponse", m)
+	}
+	return r
+}
+
+// ExpectSetFunctionBreakpointsResponse reads a protocol message from the connection
+// and fails the test if the read message is not *SetFunctionBreakpointsResponse.
+func (c *Client) ExpectSetFunctionBreakpointsResponse(t *testing.T) *dap.SetFunctionBreakpointsResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.SetFunctionBreakpointsResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.SetFunctionBreakpointsResponse", m)
+	}
+	return r
+}
+
+// ExpectSetInstructionBreakpointsResponse reads a protocol message from the connection
+// and fails the test if the read message is not *SetInstructionBreakpointsResponse.
+func (c *Client) ExpectSetInstructionBreakpointsResponse(t *testing.T) *dap.SetInstructionBreakpointsResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.SetInstructionBreakpointsResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.SetInstructionBreakpointsResponse", m)
+	}
+	return r
+}
+
+// ExpectSetVariableResponse reads a protocol message from the connection
+// and fails the test if the read message is not *SetVariableResponse.
+func (c *Client) ExpectSetVariableResponse(t *testing.T) *dap.SetVariableResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.SetVariableResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.SetVariableResponse", m)
+	}
+	return r
+}
+
+// ExpectSourceResponse reads a protocol message from the connection
+// and fails the test if the read message is not *SourceResponse.
+func (c *Client) ExpectSourceResponse(t *testing.T) *dap.SourceResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.SourceResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.SourceResponse", m)
+	}
+	return r
+}
+
+// ExpectStackTraceResponse reads a protocol message from the connection
+// and fails the test if the read message is not *StackTraceResponse.
+func (c *Client) ExpectStackTraceResponse(t *testing.T) *dap.StackTraceResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.StackTraceResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.StackTraceResponse", m)
+	}
+	return r
+}
+
+// ExpectStepBackResponse reads a protocol message from the connection
+// and fails the test if the read message is not *StepBackResponse.
+func (c *Client) ExpectStepBackResponse(t *testing.T) *dap.StepBackResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.StepBackResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.StepBackResponse", m)
+	}
+	return r
+}
+
+// ExpectStepInResponse reads a protocol message from the connection
+// and fails the test if the read message is not *StepInResponse.
+func (c *Client) ExpectStepInResponse(t *testing.T) *dap.StepInResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.StepInResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.StepInResponse", m)
+	}
+	return r
+}
+
+// ExpectStepInTargetsResponse reads a protocol message from the connection
+// and fails the test if the read message is not *StepInTargetsResponse.
+func (c *Client) ExpectStepInTargetsResponse(t *testing.T) *dap.StepInTargetsResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.StepInTargetsResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.StepInTargetsResponse", m)
+	}
+	return r
+}
+
+// ExpectStepOutResponse reads a protocol message from the connection
+// and fails the test if the read message is not *StepOutResponse.
+func (c *Client) ExpectStepOutResponse(t *testing.T) *dap.StepOutResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.StepOutResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.StepOutResponse", m)
+	}
+	return r
+}
+
+// ExpectStoppedEvent reads a protocol message from the connection
+// and fails the test if the read message is not *StoppedEvent.
+func (c *Client) ExpectStoppedEvent(t *testing.T) *dap.StoppedEvent {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.StoppedEvent)
+	if !ok {
+		t.Fatalf("got %q, want *dap.StoppedEvent", m)
+	}
+	return r
+}
+
+// ExpectTerminateResponse reads a protocol message from the connection
+// and fails the test if the read message is not *TerminateResponse.
+func (c *Client) ExpectTerminateResponse(t *testing.T) *dap.TerminateResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.TerminateResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.TerminateResponse", m)
+	}
+	return r
+}
+
+// ExpectTerminateThreadsResponse reads a protocol message from the connection
+// and fails the test if the read message is not *TerminateThreadsResponse.
+func (c *Client) ExpectTerminateThreadsResponse(t *testing.T) *dap.TerminateThreadsResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.TerminateThreadsResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.TerminateThreadsResponse", m)
+	}
+	return r
+}
+
+// ExpectTerminatedEvent reads a protocol message from the connection
+// and fails the test if the read message is not *TerminatedEvent.
+func (c *Client) ExpectTerminatedEvent(t *testing.T) *dap.TerminatedEvent {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.TerminatedEvent)
+	if !ok {
+		t.Fatalf("got %q, want *dap.TerminatedEvent", m)
+	}
+	return r
+}
+
+// ExpectThreadEvent reads a protocol message from the connection
+// and fails the test if the read message is not *ThreadEvent.
+func (c *Client) ExpectThreadEvent(t *testing.T) *dap.ThreadEvent {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.ThreadEvent)
+	if !ok {
+		t.Fatalf("got %q, want *dap.ThreadEvent", m)
+	}
+	return r
+}
+
+// ExpectThreadsResponse reads a protocol message from the connection
+// and fails the test if the read message is not *ThreadsResponse.
+func (c *Client) ExpectThreadsResponse(t *testing.T) *dap.ThreadsResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.ThreadsResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.ThreadsResponse", m)
+	}
+	return r
+}
+
+// ExpectVariablesResponse reads a protocol message from the connection
+// and fails the test if the read message is not *VariablesResponse.
+func (c *Client) ExpectVariablesResponse(t *testing.T) *dap.VariablesResponse {
+	t.Helper()
+	m := c.ExpectMessage(t)
+	r, ok := m.(*dap.VariablesResponse)
+	if !ok {
+		t.Fatalf("got %q, want *dap.VariablesResponse", m)
+	}
+	return r
+}
diff --git a/service/dap/server_test.go b/service/dap/server_test.go
index 65e011ad..ff1d9f10 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
@@ -122,7 +128,7 @@ func TestLaunchStopOnEntry(t *testing.T) {
 	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
 		// 1 >> initialize, << initialize
 		client.InitializeRequest()
-		initResp := client.ExpectInitializeResponse(t)
+		initResp := client.ExpectInitializeResponseAndCapabilities(t)
 		if initResp.Seq != 0 || initResp.RequestSeq != 1 {
 			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=1", initResp)
 		}
@@ -185,21 +191,21 @@ func TestLaunchStopOnEntry(t *testing.T) {
 
 		// 8 >> stackTrace, << error
 		client.StackTraceRequest(1, 0, 20)
-		stResp := client.ExpectErrorResponse(t)
+		stResp := client.ExpectInvisibleErrorResponse(t)
 		if stResp.Seq != 0 || stResp.RequestSeq != 8 || stResp.Body.Error.Format != "Unable to produce stack trace: unknown goroutine 1" {
 			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=8 Format=\"Unable to produce stack trace: unknown goroutine 1\"", stResp)
 		}
 
 		// 9 >> stackTrace, << error
 		client.StackTraceRequest(1, 0, 20)
-		stResp = client.ExpectErrorResponse(t)
+		stResp = client.ExpectInvisibleErrorResponse(t)
 		if stResp.Seq != 0 || stResp.RequestSeq != 9 || stResp.Body.Error.Id != 2004 {
 			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=9 Id=2004", stResp)
 		}
 
 		// 10 >> evaluate, << error
 		client.EvaluateRequest("foo", 0 /*no frame specified*/, "repl")
-		erResp := client.ExpectErrorResponse(t)
+		erResp := client.ExpectInvisibleErrorResponse(t)
 		if erResp.Seq != 0 || erResp.RequestSeq != 10 || erResp.Body.Error.Id != 2009 {
 			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=10 Id=2009", erResp)
 		}
@@ -266,7 +272,7 @@ func TestAttachStopOnEntry(t *testing.T) {
 
 		// 1 >> initialize, << initialize
 		client.InitializeRequest()
-		initResp := client.ExpectInitializeResponse(t)
+		initResp := client.ExpectInitializeResponseAndCapabilities(t)
 		if initResp.Seq != 0 || initResp.RequestSeq != 1 {
 			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=1", initResp)
 		}
@@ -333,7 +339,7 @@ func TestAttachStopOnEntry(t *testing.T) {
 
 		// 10 >> evaluate, << error
 		client.EvaluateRequest("foo", 0 /*no frame specified*/, "repl")
-		erResp := client.ExpectErrorResponse(t)
+		erResp := client.ExpectInvisibleErrorResponse(t)
 		if erResp.Seq != 0 || erResp.RequestSeq != 10 || erResp.Body.Error.Id != 2009 {
 			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=10 Id=2009", erResp)
 		}
@@ -399,7 +405,7 @@ func TestContinueOnEntry(t *testing.T) {
 	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
 		// 1 >> initialize, << initialize
 		client.InitializeRequest()
-		client.ExpectInitializeResponse(t)
+		client.ExpectInitializeResponseAndCapabilities(t)
 
 		// 2 >> launch, << initialized, << launch
 		client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
@@ -467,7 +473,7 @@ func TestContinueOnEntry(t *testing.T) {
 func TestPreSetBreakpoint(t *testing.T) {
 	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
 		client.InitializeRequest()
-		client.ExpectInitializeResponse(t)
+		client.ExpectInitializeResponseAndCapabilities(t)
 
 		client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
 		client.ExpectInitializedEvent(t)
@@ -1058,7 +1064,7 @@ func TestScopesAndVariablesRequests(t *testing.T) {
 					expectScope(t, scopes, 2, "Globals (package main)", 1002)
 
 					client.ScopesRequest(1111)
-					erres := client.ExpectErrorResponse(t)
+					erres := client.ExpectInvisibleErrorResponse(t)
 					if erres.Body.Error.Format != "Unable to list locals: unknown frame id 1111" {
 						t.Errorf("\ngot %#v\nwant Format=\"Unable to list locals: unknown frame id 1111\"", erres)
 					}
@@ -1077,7 +1083,7 @@ func TestScopesAndVariablesRequests(t *testing.T) {
 					expectVarExact(t, globals, 0, "p1", "main.p1", "10", noChildren)
 
 					client.VariablesRequest(7777)
-					erres = client.ExpectErrorResponse(t)
+					erres = client.ExpectInvisibleErrorResponse(t)
 					if erres.Body.Error.Format != "Unable to lookup variable: unknown reference 7777" {
 						t.Errorf("\ngot %#v\nwant Format=\"Unable to lookup variable: unknown reference 7777\"", erres)
 					}
@@ -2138,7 +2144,7 @@ func TestEvaluateRequest(t *testing.T) {
 						t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"", erres)
 					}
 					client.EvaluateRequest("a1", 1002, "watch")
-					erres = client.ExpectErrorResponse(t)
+					erres = client.ExpectInvisibleErrorResponse(t)
 					if erres.Body.Error.Format != "Unable to evaluate expression: could not find symbol value for a1" {
 						t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"", erres)
 					}
@@ -2280,7 +2286,7 @@ func TestEvaluateCallRequest(t *testing.T) {
 					expectEval(t, got, "\"this is a variable named `call`\"", noChildren)
 					// Call error
 					client.EvaluateRequest("call call1(one)", 1000, "watch")
-					erres := client.ExpectErrorResponse(t)
+					erres := client.ExpectInvisibleErrorResponse(t)
 					if erres.Body.Error.Format != "Unable to evaluate expression: not enough arguments" {
 						t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: not enough arguments\"", erres)
 					}
@@ -2718,7 +2724,7 @@ type onBreakpoint struct {
 //     onBreakpoints - list of test sequences to execute at each of the set breakpoints.
 func runDebugSessionWithBPs(t *testing.T, client *daptest.Client, cmd string, cmdRequest func(), source string, breakpoints []int, onBPs []onBreakpoint) {
 	client.InitializeRequest()
-	client.ExpectInitializeResponse(t)
+	client.ExpectInitializeResponseAndCapabilities(t)
 
 	cmdRequest()
 	client.ExpectInitializedEvent(t)
@@ -2890,7 +2896,7 @@ func TestLaunchRequestNoDebug_BadStatus(t *testing.T) {
 // even when breakpoint is set.
 func runNoDebugDebugSession(t *testing.T, client *daptest.Client, cmdRequest func(), source string, breakpoints []int, status int) {
 	client.InitializeRequest()
-	client.ExpectInitializeResponse(t)
+	client.ExpectInitializeResponseAndCapabilities(t)
 
 	cmdRequest()
 	// no initialized event.
@@ -3134,75 +3140,75 @@ func TestBadLaunchRequests(t *testing.T) {
 
 		// Test for the DAP-specific detailed error message.
 		client.LaunchRequest("exec", "", stopOnEntry)
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to launch: The program attribute is missing in debug configuration.")
 
 		// Bad "program"
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": 12345})
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to launch: The program attribute is missing in debug configuration.")
 
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": nil})
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to launch: The program attribute is missing in debug configuration.")
 
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug"})
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to launch: The program attribute is missing in debug configuration.")
 
 		// Bad "mode"
 		client.LaunchRequest("remote", fixture.Path, stopOnEntry)
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to launch: Unsupported 'mode' value \"remote\" in debug configuration.")
 
 		client.LaunchRequest("notamode", fixture.Path, stopOnEntry)
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to launch: Unsupported 'mode' value \"notamode\" in debug configuration.")
 
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": 12345, "program": fixture.Path})
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to launch: Unsupported 'mode' value %!q(float64=12345) in debug configuration.")
 
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": ""}) // empty mode defaults to "debug" (not an error)
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to launch: The program attribute is missing in debug configuration.")
 
 		client.LaunchRequestWithArgs(map[string]interface{}{}) // missing mode defaults to "debug" (not an error)
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to launch: The program attribute is missing in debug configuration.")
 
 		// Bad "args"
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "exec", "program": fixture.Path, "args": nil})
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to launch: 'args' attribute '<nil>' in debug configuration is not an array.")
 
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "exec", "program": fixture.Path, "args": 12345})
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to launch: 'args' attribute '12345' in debug configuration is not an array.")
 
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "exec", "program": fixture.Path, "args": []int{1, 2}})
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to launch: value '1' in 'args' attribute in debug configuration is not a string.")
 
 		// Bad "buildFlags"
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "buildFlags": 123})
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to launch: 'buildFlags' attribute '123' in debug configuration is not a string.")
 
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "substitutePath": 123})
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to launch: 'substitutePath' attribute '123' in debug configuration is not a []{'from': string, 'to': string}")
 
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "substitutePath": []interface{}{123}})
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to launch: 'substitutePath' attribute '[123]' in debug configuration is not a []{'from': string, 'to': string}")
 
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "substitutePath": []interface{}{map[string]interface{}{"to": "path2"}}})
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to launch: 'substitutePath' attribute '[map[to:path2]]' in debug configuration is not a []{'from': string, 'to': string}")
 
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "substitutePath": []interface{}{map[string]interface{}{"from": "path1", "to": 123}}})
-		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to launch: 'substitutePath' attribute '[map[from:path1 to:123]]' in debug configuration is not a []{'from': string, 'to': string}")
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "cwd": 123})
 		expectFailedToLaunchWithMessage(client.ExpectErrorResponse(t),
@@ -3210,21 +3216,21 @@ func TestBadLaunchRequests(t *testing.T) {
 
 		// Skip detailed message checks for potentially different OS-specific errors.
 		client.LaunchRequest("exec", fixture.Path+"_does_not_exist", stopOnEntry)
-		expectFailedToLaunch(client.ExpectErrorResponse(t)) // No such file or directory
+		expectFailedToLaunch(client.ExpectInvisibleErrorResponse(t)) // No such file or directory
 
 		client.LaunchRequest("debug", fixture.Path+"_does_not_exist", stopOnEntry)
-		expectFailedToLaunch(client.ExpectErrorResponse(t))
+		expectFailedToLaunch(client.ExpectInvisibleErrorResponse(t))
 
 		client.LaunchRequest("" /*debug by default*/, fixture.Path+"_does_not_exist", stopOnEntry)
-		expectFailedToLaunch(client.ExpectErrorResponse(t))
+		expectFailedToLaunch(client.ExpectInvisibleErrorResponse(t))
 
 		client.LaunchRequest("exec", fixture.Source, stopOnEntry)
-		expectFailedToLaunch(client.ExpectErrorResponse(t)) // Not an executable
+		expectFailedToLaunch(client.ExpectInvisibleErrorResponse(t)) // Not an executable
 
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "buildFlags": "-bad -flags"})
-		expectFailedToLaunchWithMessageRegex(client.ExpectErrorResponse(t), `Failed to launch: Build error: .*flag provided but not defined.*`)
+		expectFailedToLaunchWithMessageRegex(client.ExpectInvisibleErrorResponse(t), `Failed to launch: Build error: .*flag provided but not defined.*`)
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": true, "buildFlags": "-bad -flags"})
-		expectFailedToLaunchWithMessageRegex(client.ExpectErrorResponse(t), `Failed to launch: Build error: .*flag provided but not defined.*`)
+		expectFailedToLaunchWithMessageRegex(client.ExpectInvisibleErrorResponse(t), `Failed to launch: Build error: .*flag provided but not defined.*`)
 
 		// Bad "wd".
 		client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": false, "cwd": "dir/invalid"})
@@ -3271,49 +3277,49 @@ func TestBadAttachRequest(t *testing.T) {
 
 		// Bad "mode"
 		client.AttachRequest(map[string]interface{}{"mode": "remote"})
-		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToAttachWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to attach: Unsupported 'mode' value \"remote\" in debug configuration")
 
 		client.AttachRequest(map[string]interface{}{"mode": "blah blah blah"})
-		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToAttachWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to attach: Unsupported 'mode' value \"blah blah blah\" in debug configuration")
 
 		client.AttachRequest(map[string]interface{}{"mode": 123})
-		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToAttachWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to attach: Unsupported 'mode' value %!q(float64=123) in debug configuration")
 
 		client.AttachRequest(map[string]interface{}{"mode": ""}) // empty mode defaults to "local" (not an error)
-		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToAttachWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to attach: The 'processId' attribute is missing in debug configuration")
 
 		client.AttachRequest(map[string]interface{}{}) // no mode defaults to "local" (not an error)
-		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToAttachWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to attach: The 'processId' attribute is missing in debug configuration")
 
 		// Bad "processId"
 		client.AttachRequest(map[string]interface{}{"mode": "local"})
-		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToAttachWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to attach: The 'processId' attribute is missing in debug configuration")
 
 		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": nil})
-		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToAttachWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to attach: The 'processId' attribute is missing in debug configuration")
 
 		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 0})
-		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToAttachWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to attach: The 'processId' attribute is missing in debug configuration")
 
 		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": "1"})
-		expectFailedToAttachWithMessage(client.ExpectErrorResponse(t),
+		expectFailedToAttachWithMessage(client.ExpectInvisibleErrorResponse(t),
 			"Failed to attach: The 'processId' attribute is missing in debug configuration")
 
 		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 1})
 		// The exact message varies on different systems, so skip that check
-		expectFailedToAttach(client.ExpectErrorResponse(t)) // could not attach to pid 1
+		expectFailedToAttach(client.ExpectInvisibleErrorResponse(t)) // could not attach to pid 1
 
 		// This will make debugger.(*Debugger) panic, which we will catch as an internal error.
 		client.AttachRequest(map[string]interface{}{"mode": "local", "processId": -1})
-		er := client.ExpectErrorResponse(t)
+		er := client.ExpectInvisibleErrorResponse(t)
 		if er.RequestSeq != seqCnt {
 			t.Errorf("RequestSeq got %d, want %d", seqCnt, er.RequestSeq)
 		}
@@ -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