// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package dap import ( "encoding/json" "reflect" "testing" ) // -------- Responses & Requests -------- var errorResponseString = `{"seq":11,"type":"response","request_seq":9,"command":"stackTrace","success":false,"message":"Unable to produce stack trace: \"{e}\"","body":{"error":{"id":2004,"format":"Unable to produce stack trace: \"{e}\"","variables":{"e":"Unknown goroutine 1"},"showUser":true}}}` var errorResponseStruct = ErrorResponse{ Response: Response{ ProtocolMessage: ProtocolMessage{ Seq: 11, Type: "response", }, Command: "stackTrace", RequestSeq: 9, Success: false, Message: "Unable to produce stack trace: \"{e}\"", }, Body: ErrorResponseBody{ Error: &ErrorMessage{ Id: 2004, Format: "Unable to produce stack trace: \"{e}\"", Variables: map[string]string{"e": "Unknown goroutine 1"}, ShowUser: true, }, }, } // -------- CancelRequest var cancelRequestString = `{"seq":25,"type":"request","command":"cancel","arguments":{"requestId":24}}` var cancelRequestStruct = CancelRequest{ Request: *newRequest(25, "cancel"), Arguments: &CancelArguments{RequestId: 24}, } var cancelResponseString = `{"seq":25,"type":"response","request_seq":26,"command":"cancel","success":true}` var cancelResponseStruct = CancelResponse{Response: *newResponse(25, 26, "cancel", true)} // -------- RunInTerminalRequest var runInTerminalRequestString = `{"seq":45,"type":"request","command":"runInTerminal","arguments":{"kind":"integrated","title":"Some Title","cwd":"/working/dir","args":["mycommand","arg1","arg2"]}}` var runInTerminalRequestStruct = RunInTerminalRequest{ Request: *newRequest(45, "runInTerminal"), Arguments: RunInTerminalRequestArguments{ Kind: "integrated", Title: "Some Title", Cwd: "/working/dir", Args: []string{"mycommand", "arg1", "arg2"}, }, } var runInTerminalResponseString = `{"seq":45,"type":"response","request_seq":46,"command":"runInTerminal","success":true,"body":{"processId":123456}}` var runInTerminalResponseStruct = RunInTerminalResponse{ Response: *newResponse(45, 46, "runInTerminal", true), Body: RunInTerminalResponseBody{ProcessId: 123456}, } // -------- Initialize var initializeRequestString = `{"seq":1,"type":"request","command":"initialize","arguments":{"clientID":"vscode","clientName":"Visual Studio Code","adapterID":"go","pathFormat":"path","linesStartAt1":true,"columnsStartAt1":true,"supportsVariableType":true,"supportsVariablePaging":true,"supportsRunInTerminalRequest":true,"locale":"en-us"}}` var initializeRequestOmitDefaultsString = `{"seq":1,"type":"request","command":"initialize","arguments":{"clientID":"vscode","clientName":"Visual Studio Code","adapterID":"go","supportsVariableType":true,"supportsVariablePaging":true,"supportsRunInTerminalRequest":true,"locale":"en-us"}}` var initializeRequestStruct = InitializeRequest{ Request: *newRequest(1, "initialize"), Arguments: InitializeRequestArguments{ ClientID: "vscode", ClientName: "Visual Studio Code", AdapterID: "go", PathFormat: "path", LinesStartAt1: true, ColumnsStartAt1: true, SupportsVariableType: true, SupportsVariablePaging: true, SupportsRunInTerminalRequest: true, Locale: "en-us", }, } var initializeRequestNotDefaultsString = `{"seq":1,"type":"request","command":"initialize","arguments":{"clientID":"vscode","clientName":"Visual Studio Code","adapterID":"go","pathFormat":"url","linesStartAt1":false,"columnsStartAt1":false,"supportsVariableType":true,"supportsVariablePaging":true,"supportsRunInTerminalRequest":true,"locale":"en-us"}}` var initializeRequestNotDefaultsStruct = InitializeRequest{ Request: *newRequest(1, "initialize"), Arguments: InitializeRequestArguments{ ClientID: "vscode", ClientName: "Visual Studio Code", AdapterID: "go", PathFormat: "url", LinesStartAt1: false, ColumnsStartAt1: false, SupportsVariableType: true, SupportsVariablePaging: true, SupportsRunInTerminalRequest: true, Locale: "en-us", }, } var initializeResponseString = `{"seq":1,"type":"response","request_seq":2,"command":"initialize","success":true,"body":{"supportsConfigurationDoneRequest":true,"supportsSetVariable":true}}` var initializeResponseStruct = InitializeResponse{ Response: *newResponse(1, 2, "initialize", true), Body: Capabilities{ SupportsConfigurationDoneRequest: true, SupportsSetVariable: true, }, } // -------- ConfigurationDone var configurationDoneRequestString = `{"seq":2,"type":"request","command":"configurationDone"}` var configurationDoneRequestStruct = ConfigurationDoneRequest{ Request: *newRequest(2, "configurationDone"), Arguments: nil, } var configurationDoneResponseString = `{"seq":2,"type":"response","request_seq":3,"command":"configurationDone","success":true}` var configurationDoneResponseStruct = ConfigurationDoneResponse{Response: *newResponse(2, 3, "configurationDone", true)} // -------- Launch var launchRequestString = `{"seq":3,"type":"request","command":"launch","arguments":{"noDebug":true,"name":"Launch"}}` var launchRequestStruct = LaunchRequest{ Request: *newRequest(3, "launch"), Arguments: json.RawMessage(`{"noDebug":true,"name":"Launch"}`), } var launchResponseString = `{"seq":3,"type":"response","request_seq":4,"command":"launch","success":true}` var launchResponseStruct = LaunchResponse{Response: *newResponse(3, 4, "launch", true)} // -------- Attach var attachRequestString = `{"seq":4,"type":"request","command":"attach","arguments":{}}` var attachRequestStruct = AttachRequest{ Request: *newRequest(4, "attach"), Arguments: json.RawMessage(`{}`), } var attachResponseString = `{"seq":4,"type":"response","request_seq":5,"command":"attach","success":true}` var attachResponseStruct = AttachResponse{Response: *newResponse(4, 5, "attach", true)} // -------- Restart var restartRequestString = `{"seq":5,"type":"request","command":"restart"}` var restartRequestStruct = RestartRequest{ Request: *newRequest(5, "restart"), Arguments: nil, } var restartResponseString = `{"seq":5,"type":"response","request_seq":6,"command":"restart","success":true}` var restartResponseStruct = RestartResponse{Response: *newResponse(5, 6, "restart", true)} // -------- Disconnect var disconnectRequestString = `{"seq":6,"type":"request","command":"disconnect","arguments":{"restart":true}}` var disconnectRequestStruct = DisconnectRequest{ Request: *newRequest(6, "disconnect"), Arguments: &DisconnectArguments{Restart: true}, } var disconnectResponseString = `{"seq":6,"type":"response","request_seq":7,"command":"disconnect","success":true}` var disconnectResponseStruct = DisconnectResponse{Response: *newResponse(6, 7, "disconnect", true)} // -------- Terminate var terminateRequestString = `{"seq":7,"type":"request","command":"terminate","arguments":{"restart":true}}` var terminateRequestStruct = TerminateRequest{ Request: *newRequest(7, "terminate"), Arguments: &TerminateArguments{Restart: true}, } var terminateResponseString = `{"seq":7,"type":"response","request_seq":8,"command":"terminate","success":true}` var terminateResponseStruct = TerminateResponse{Response: *newResponse(7, 8, "terminate", true)} // -------- BreakpointLocations var breakpointLocationsRequestString = `{"seq":8,"type":"request","command":"breakpointLocations","arguments":{"source":{"name":"hello.go","path":"/Users/foo/go/src/hello/hello.go"},"line":10}}` var breakpointLocationsRequestStruct = BreakpointLocationsRequest{ Request: *newRequest(8, "breakpointLocations"), Arguments: &BreakpointLocationsArguments{ Source: Source{Name: "hello.go", Path: "/Users/foo/go/src/hello/hello.go"}, Line: 10, }, } var breakpointLocationsResponseString = `{"seq":8,"type":"response","request_seq":9,"command":"breakpointLocations","success":true,"body":{"breakpoints":[{"line":14}]}}` var breakpointLocationsResponseStruct = BreakpointLocationsResponse{ Response: *newResponse(8, 9, "breakpointLocations", true), Body: BreakpointLocationsResponseBody{ Breakpoints: []BreakpointLocation{{Line: 14}}, }, } // -------- SetBreakpoints var setBreakpointsRequestString = `{"seq":9,"type":"request","command":"setBreakpoints","arguments":{"source":{"name":"hello.go","path":"/Users/foo/go/src/hello/hello.go"},"lines":[14],"breakpoints":[{"line":14}],"sourceModified":false}}` var setBreakpointsRequestStruct = SetBreakpointsRequest{ Request: *newRequest(9, "setBreakpoints"), Arguments: SetBreakpointsArguments{ Source: Source{Name: "hello.go", Path: "/Users/foo/go/src/hello/hello.go"}, Breakpoints: []SourceBreakpoint{{Line: 14}}, Lines: []int{14}, SourceModified: false, }, } var setBreakpointsResponseString = `{"seq":9,"type":"response","request_seq":10,"command":"setBreakpoints","success":true,"body":{"breakpoints":[{"verified":true,"line":14}]}}` var setBreakpointsResponseStruct = SetBreakpointsResponse{ Response: *newResponse(9, 10, "setBreakpoints", true), Body: SetBreakpointsResponseBody{ Breakpoints: []Breakpoint{{Verified: true, Line: 14}}, }, } // -------- SetFunctionBreakpoints var setFunctionBreakpointsRequestString = `{"seq":10,"type":"request","command":"setFunctionBreakpoints","arguments":{"breakpoints":[{"name":"blah"}]}}` var setFunctionBreakpointsRequestStruct = SetFunctionBreakpointsRequest{ Request: *newRequest(10, "setFunctionBreakpoints"), Arguments: SetFunctionBreakpointsArguments{ Breakpoints: []FunctionBreakpoint{{Name: "blah"}}, }, } var setFunctionBreakpointsResponseString = `{"seq":10,"type":"response","request_seq":11,"command":"setFunctionBreakpoints","success":true, "body":{"breakpoints":[{"verified":true,"line":20}]}}` var setFunctionBreakpointsResponseStruct = SetFunctionBreakpointsResponse{ Response: *newResponse(10, 11, "setFunctionBreakpoints", true), Body: SetFunctionBreakpointsResponseBody{ Breakpoints: []Breakpoint{{Verified: true, Line: 20}}, }, } // -------- SetExceptionBreakpoints var setExceptionBreakpointsRequestString = `{"seq":11,"type":"request","command":"setExceptionBreakpoints","arguments":{"filters":[]}}` var setExceptionBreakpointsRequestStruct = SetExceptionBreakpointsRequest{ Request: *newRequest(11, "setExceptionBreakpoints"), Arguments: SetExceptionBreakpointsArguments{Filters: []string{}}, } var setExceptionBreakpointsResponseString = `{"seq":11,"type":"response","request_seq":12,"command":"setExceptionBreakpoints","success":true}` var setExceptionBreakpointsResponseStruct = SetExceptionBreakpointsResponse{Response: *newResponse(11, 12, "setExceptionBreakpoints", true)} // -------- DataBreakpointInfo var dataBreakpointInfoRequestString = `{"seq":12,"type":"request","command":"dataBreakpointInfo","arguments":{"name":"fuzzybunny"}}` var dataBreakpointInfoRequestStruct = DataBreakpointInfoRequest{ Request: *newRequest(12, "dataBreakpointInfo"), Arguments: DataBreakpointInfoArguments{Name: "fuzzybunny"}, } var dataBreakpointInfoResponseString = `{"seq":12,"type":"response","request_seq":13,"command":"dataBreakpointInfo","success":true, "body":{"dataId":null,"description":"some description"}}` var dataBreakpointInfoResponseStruct = DataBreakpointInfoResponse{ Response: *newResponse(12, 13, "dataBreakpointInfo", true), Body: DataBreakpointInfoResponseBody{ DataId: nil, Description: "some description", }, } // -------- SetDataBreakpoints var setDataBreakpointsRequestString = `{"seq":13,"type":"request","command":"setDataBreakpoints","arguments":{"breakpoints":[{"dataId":"dataid"}]}}` var setDataBreakpointsRequestStruct = SetDataBreakpointsRequest{ Request: *newRequest(13, "setDataBreakpoints"), Arguments: SetDataBreakpointsArguments{ Breakpoints: []DataBreakpoint{{DataId: "dataid"}}, }, } var setDataBreakpointsResponseString = `{"seq":13,"type":"response","request_seq":14,"command":"setDataBreakpoints","success":true, "body":{"breakpoints":[{"verified":true,"line":100}]}}` var setDataBreakpointsResponseStruct = SetDataBreakpointsResponse{ Response: *newResponse(13, 14, "setDataBreakpoints", true), Body: SetDataBreakpointsResponseBody{ Breakpoints: []Breakpoint{{Verified: true, Line: 100}}, }, } // -------- Continue var continueRequestString = `{"seq":14,"type":"request","command":"continue","arguments":{"threadId":1}}` var continueRequestStruct = ContinueRequest{ Request: *newRequest(14, "continue"), Arguments: ContinueArguments{ThreadId: 1}, } var continueResponseString = `{"seq":14,"type":"response","request_seq":15,"command":"continue","success":true, "body":{"allThreadsContinued": true}}` var continueResponseStruct = ContinueResponse{ Response: *newResponse(14, 15, "continue", true), Body: ContinueResponseBody{AllThreadsContinued: true}, } // -------- Next var nextRequestString = `{"seq":15,"type":"request","command":"next","arguments":{"threadId":1}}` var nextRequestStruct = NextRequest{ Request: *newRequest(15, "next"), Arguments: NextArguments{ThreadId: 1}, } var nextResponseString = `{"seq":15,"type":"response","request_seq":16,"command":"next","success":true}` var nextResponseStruct = NextResponse{Response: *newResponse(15, 16, "next", true)} // -------- StepIn var stepInRequestString = `{"seq":16,"type":"request","command":"stepIn","arguments":{"threadId":1}}` var stepInRequestStruct = StepInRequest{ Request: *newRequest(16, "stepIn"), Arguments: StepInArguments{ThreadId: 1}, } var stepInResponseString = `{"seq":16,"type":"response","request_seq":17,"command":"stepIn","success":true}` var stepInResponseStruct = StepInResponse{Response: *newResponse(16, 17, "stepIn", true)} // -------- StepOut var stepOutRequestString = `{"seq":17,"type":"request","command":"stepOut","arguments":{"threadId":1}}` var stepOutRequestStruct = StepOutRequest{ Request: *newRequest(17, "stepOut"), Arguments: StepOutArguments{ThreadId: 1}, } var stepOutResponseString = `{"seq":17,"type":"response","request_seq":18,"command":"stepOut","success":true}` var stepOutResponseStruct = StepOutResponse{Response: *newResponse(17, 18, "stepOut", true)} // -------- StepBack var stepBackRequestString = `{"seq":18,"type":"request","command":"stepBack","arguments":{"threadId":1}}` var stepBackRequestStruct = StepBackRequest{ Request: *newRequest(18, "stepBack"), Arguments: StepBackArguments{ThreadId: 1}, } var stepBackResponseString = `{"seq":18,"type":"response","request_seq":19,"command":"stepBack","success":true}` var stepBackResponseStruct = StepBackResponse{Response: *newResponse(18, 19, "stepBack", true)} // -------- ReverseContinue var reverseContinueRequestString = `{"seq":19,"type":"request","command":"reverseContinue","arguments":{"threadId":1}}` var reverseContinueRequestStruct = ReverseContinueRequest{ Request: *newRequest(19, "reverseContinue"), Arguments: ReverseContinueArguments{ThreadId: 1}, } var reverseContinueResponseString = `{"seq":19,"type":"response","request_seq":20,"command":"reverseContinue","success":true}` var reverseContinueResponseStruct = ReverseContinueResponse{Response: *newResponse(19, 20, "reverseContinue", true)} // -------- RestartFrame var restartFrameRequestString = `{"seq":20,"type":"request","command":"restartFrame","arguments":{"frameId":5}}` var restartFrameRequestStruct = RestartFrameRequest{ Request: *newRequest(20, "restartFrame"), Arguments: RestartFrameArguments{FrameId: 5}, } var restartFrameResponseString = `{"seq":20,"type":"response","request_seq":21,"command":"restartFrame","success":true}` var restartFrameResponseStruct = RestartFrameResponse{Response: *newResponse(20, 21, "restartFrame", true)} // -------- Goto var gotoRequestString = `{"seq":21,"type":"request","command":"goto","arguments":{"threadId":1,"targetId":2}}` var gotoRequestStruct = GotoRequest{ Request: *newRequest(21, "goto"), Arguments: GotoArguments{ThreadId: 1, TargetId: 2}, } var gotoResponseString = `{"seq":21,"type":"response","request_seq":22,"command":"goto","success":true}` var gotoResponseStruct = GotoResponse{Response: *newResponse(21, 22, "goto", true)} // -------- Pause var pauseRequestString = `{"seq":22,"type":"request","command":"pause","arguments":{"threadId":1}}` var pauseRequestStruct = PauseRequest{ Request: *newRequest(22, "pause"), Arguments: PauseArguments{ThreadId: 1}, } var pauseResponseString = `{"seq":22,"type":"response","request_seq":23,"command":"pause","success":true}` var pauseResponseStruct = PauseResponse{Response: *newResponse(22, 23, "pause", true)} // -------- StackTrace var stackTraceRequestString = `{"seq":23,"type":"request","command":"stackTrace","arguments":{"threadId":1,"startFrame":0,"levels":20}}` var stackTraceRequestStruct = StackTraceRequest{ Request: *newRequest(23, "stackTrace"), Arguments: StackTraceArguments{ ThreadId: 1, StartFrame: 0, Levels: 20, }, } var stackTraceResponseString = `{"seq":23,"type":"response","request_seq":24,"command":"stackTrace","success":true,"body":{"stackFrames":[{"id":1000,"source":{"name":"hello.go","path":"/Users/foo/go/src/hello/hello.go","sourceReference":0},"line":6,"column":0,"name":"main.main"},{"id":1001,"source":{"name":"proc.go","path":"/usr/local/go/src/runtime/proc.go","sourceReference":0},"line":203,"column":0,"name":"runtime.main"},{"id":1002,"source":{"name":"asm_amd64.s","path":"/usr/local/go/src/runtime/asm_amd64.s","sourceReference":0},"line":1357,"column":0,"name":"runtime.goexit"}],"totalFrames":3}}` var stackTraceResponseStruct = StackTraceResponse{ Response: *newResponse(23, 24, "stackTrace", true), Body: StackTraceResponseBody{ StackFrames: []StackFrame{ { Id: 1000, Source: &Source{ Name: "hello.go", Path: "/Users/foo/go/src/hello/hello.go", SourceReference: 0, }, Line: 6, Column: 0, Name: "main.main", }, { Id: 1001, Source: &Source{ Name: "proc.go", Path: "/usr/local/go/src/runtime/proc.go", SourceReference: 0, }, Line: 203, Column: 0, Name: "runtime.main", }, { Id: 1002, Source: &Source{ Name: "asm_amd64.s", Path: "/usr/local/go/src/runtime/asm_amd64.s", SourceReference: 0, }, Line: 1357, Column: 0, Name: "runtime.goexit", }, }, TotalFrames: 3, }, } // -------- Scopes var scopesRequestString = `{"seq":24,"type":"request","command":"scopes","arguments":{"frameId":1000}}` var scopesRequestStruct = ScopesRequest{ Request: *newRequest(24, "scopes"), Arguments: ScopesArguments{FrameId: 1000}, } var scopesResponseString = `{"seq":24,"type":"response","request_seq":25,"command":"scopes","success":true,"body":{"scopes":[{"name":"Local","variablesReference":1000,"expensive":false},{"name":"Global","variablesReference":1001,"expensive":false}]}}` var scopesResponseStruct = ScopesResponse{ Response: *newResponse(24, 25, "scopes", true), Body: ScopesResponseBody{ Scopes: []Scope{ { Name: "Local", VariablesReference: 1000, Expensive: false, }, { Name: "Global", VariablesReference: 1001, Expensive: false, }, }, }, } // -------- Variables var variablesRequestString = `{"seq":25,"type":"request","command":"variables","arguments":{"variablesReference":1001}}` var variablesRequestStruct = VariablesRequest{ Request: *newRequest(25, "variables"), Arguments: VariablesArguments{VariablesReference: 1001}, } var variablesResponseString = `{"seq":25,"type":"response","request_seq":26,"command":"variables","success":true,"body":{"variables":[{"name":"x","value":"824634220368","evaluateName":"x","variablesReference":0}]}}` var variablesResponseStruct = VariablesResponse{ Response: *newResponse(25, 26, "variables", true), Body: VariablesResponseBody{ Variables: []Variable{ { Name: "x", Value: "824634220368", EvaluateName: "x", VariablesReference: 0, }, }, }, } // -------- SetVariable var setVariableRequestString = `{"seq":26,"type":"request","command":"setVariable","arguments":{"variablesReference":1008,"name":"x","value":"55"}}` var setVariableRequestStruct = SetVariableRequest{ Request: *newRequest(26, "setVariable"), Arguments: SetVariableArguments{ VariablesReference: 1008, Name: "x", Value: "55", }, } var setVariableResponseString = `{"seq":26,"type":"response","request_seq":27,"command":"setVariable","success":true,"body":{"value":"55"}}` var setVariableResponseStruct = SetVariableResponse{ Response: *newResponse(26, 27, "setVariable", true), Body: SetVariableResponseBody{Value: "55"}, } // -------- Source var sourceRequestString = `{"seq":27,"type":"request","command":"source","arguments":{"sourceReference":123}}` var sourceRequestStruct = SourceRequest{ Request: *newRequest(27, "source"), Arguments: SourceArguments{SourceReference: 123}, } var sourceResponseString = `{"seq":27,"type":"response","request_seq":28,"command":"source","success":true,"body":{"content":"somecontent"}}` var sourceResponseStruct = SourceResponse{ Response: *newResponse(27, 28, "source", true), Body: SourceResponseBody{Content: "somecontent"}, } // -------- Threads var threadsRequestString = `{"seq":28,"type":"request","command":"threads","arguments":{}}` var threadsRequestStruct = ThreadsRequest{Request: *newRequest(28, "threads")} var threadsResponseString = `{"seq":28,"type":"response","request_seq":29,"command":"threads","success":true,"body":{"threads":[{"id":1,"name":"Dummy"}]}}` var threadsResponseStruct = ThreadsResponse{ Response: *newResponse(28, 29, "threads", true), Body: ThreadsResponseBody{Threads: []Thread{{Id: 1, Name: "Dummy"}}}, } // -------- TerminateThreads var terminateThreadsRequestString = `{"seq":29,"type":"request","command":"terminateThreads","arguments":{"threadIds":[1]}}` var terminateThreadsRequestStruct = TerminateThreadsRequest{ Request: *newRequest(29, "terminateThreads"), Arguments: TerminateThreadsArguments{ThreadIds: []int{1}}, } var terminateThreadsResponseString = `{"seq":29,"type":"response","request_seq":30,"command":"terminateThreads","success":true}` var terminateThreadsResponseStruct = TerminateThreadsResponse{Response: *newResponse(29, 30, "terminateThreads", true)} // -------- Modules var modulesRequestString = `{"seq":30,"type":"request","command":"modules","arguments":{"startModule":1,"moduleCount":3}}` var modulesRequestStruct = ModulesRequest{ Request: *newRequest(30, "modules"), Arguments: ModulesArguments{ StartModule: 1, ModuleCount: 3, }, } var modulesResponseString = `{"seq":30,"type":"response","request_seq":31,"command":"modules","success":true,"body":{"totalModules":2,"modules":[{"id":1,"name":"one"}]}}` var modulesResponseStruct = ModulesResponse{ Response: *newResponse(30, 31, "modules", true), Body: ModulesResponseBody{ TotalModules: 2, Modules: []Module{{Id: 1.0, Name: "one"}}, }, } // -------- LoadedSources var loadedSourcesRequestString = `{"seq":31,"type":"request","command":"loadedSources"}` var loadedSourcesRequestStruct = LoadedSourcesRequest{ Request: *newRequest(31, "loadedSources"), Arguments: nil, } var loadedSourcesResponseString = `{"seq":31,"type":"response","request_seq":32,"command":"loadedSources","success":true,"body":{"sources":[{"name":"hello.go","path":"/Users/foo/go/src/hello/hello.go"}]}}` var loadedSourcesResponseStruct = LoadedSourcesResponse{ Response: *newResponse(31, 32, "loadedSources", true), Body: LoadedSourcesResponseBody{ Sources: []Source{ { Name: "hello.go", Path: "/Users/foo/go/src/hello/hello.go", }, }, }, } // -------- Evaluate var evaluateRequestString = `{"seq":32,"type":"request","command":"evaluate","arguments":{"expression":"x==1","frameId":1000,"context":"repl"}}` var evaluateRequestStruct = EvaluateRequest{ Request: *newRequest(32, "evaluate"), Arguments: EvaluateArguments{ Expression: "x==1", FrameId: 1000, Context: "repl", }, } var evaluateResponseString = `{"seq":32,"type":"response","request_seq":33,"command":"evaluate","success":true,"body":{"result":"false","variablesReference":1}}` var evaluateResponseStruct = EvaluateResponse{ Response: *newResponse(32, 33, "evaluate", true), Body: EvaluateResponseBody{ Result: "false", VariablesReference: 1, }, } // -------- SetExpression var setExpressionRequestString = `{"seq":33,"type":"request","command":"setExpression","arguments":{"expression":"x==1","value":"true"}}` var setExpressionRequestStruct = SetExpressionRequest{ Request: *newRequest(33, "setExpression"), Arguments: SetExpressionArguments{ Expression: "x==1", Value: "true", }, } var setExpressionResponseString = `{"seq":33,"type":"response","request_seq":34,"command":"setExpression","success":true,"body":{"value":"true"}}` var setExpressionResponseStruct = SetExpressionResponse{ Response: *newResponse(33, 34, "setExpression", true), Body: SetExpressionResponseBody{Value: "true"}, } // -------- StepInTargets var stepInTargetsRequestString = `{"seq":34,"type":"request","command":"stepInTargets","arguments":{"frameId":1000}}` var stepInTargetsRequestStruct = StepInTargetsRequest{ Request: *newRequest(34, "stepInTargets"), Arguments: StepInTargetsArguments{ FrameId: 1000, }, } var stepInTargetsResponseString = `{"seq":34,"type":"response","request_seq":35,"command":"stepInTargets","success":true,"body":{"targets":[{"id":123,"label":"somelabel"}]}}` var stepInTargetsResponseStruct = StepInTargetsResponse{ Response: *newResponse(34, 35, "stepInTargets", true), Body: StepInTargetsResponseBody{ Targets: []StepInTarget{ {Id: 123, Label: "somelabel"}, }, }, } // -------- GotoTargets var gotoTargetsRequestString = `{"seq":35,"type":"request","command":"gotoTargets","arguments":{"source":{"name":"hello.go","path":"/Users/foo/go/src/hello/hello.go"},"line":10}}` var gotoTargetsRequestStruct = GotoTargetsRequest{ Request: *newRequest(35, "gotoTargets"), Arguments: GotoTargetsArguments{ Source: Source{Name: "hello.go", Path: "/Users/foo/go/src/hello/hello.go"}, Line: 10, }, } var gotoTargetsResponseString = `{"seq":35,"type":"response","request_seq":36,"command":"gotoTargets","success":true,"body":{"targets":[{"id":123,"label":"somelabel","line":10}]}}` var gotoTargetsResponseStruct = GotoTargetsResponse{ Response: *newResponse(35, 36, "gotoTargets", true), Body: GotoTargetsResponseBody{ Targets: []GotoTarget{ {Id: 123, Label: "somelabel", Line: 10}, }, }, } // -------- Completions var completionsRequestString = `{"seq": 36,"type":"request","command":"completions","arguments":{"text":"sometext","column":123}}` var completionsRequestStruct = CompletionsRequest{ Request: *newRequest(36, "completions"), Arguments: CompletionsArguments{Text: "sometext", Column: 123}, } var completionsResponseString = `{"seq":36,"type":"response","request_seq":37,"command":"completions","success":true,"body":{"targets":[{"label":"somelabel"}]}}` var completionsResponseStruct = CompletionsResponse{ Response: *newResponse(36, 37, "completions", true), Body: CompletionsResponseBody{ Targets: []CompletionItem{ {Label: "somelabel"}, }, }, } // -------- ExceptionInfo var exceptionInfoRequestString = `{"seq":36,"type":"request","command":"exceptionInfo","arguments":{"threadId":1}}` var exceptionInfoRequestStruct = ExceptionInfoRequest{ Request: *newRequest(36, "exceptionInfo"), Arguments: ExceptionInfoArguments{ThreadId: 1}, } var exceptionInfoResponseString = `{"seq":36,"type":"response","request_seq":37,"command":"exceptionInfo","success":true,"body":{"exceptionId":"someid","breakMode":"somebreakmode"}}` var exceptionInfoResponseStruct = ExceptionInfoResponse{ Response: *newResponse(36, 37, "exceptionInfo", true), Body: ExceptionInfoResponseBody{ ExceptionId: "someid", BreakMode: "somebreakmode", }, } // -------- ReadMemory var readMemoryRequestString = `{"seq":37,"type":"request","command":"readMemory","arguments":{"memoryReference":"someref","count":123}}` var readMemoryRequestStruct = ReadMemoryRequest{ Request: *newRequest(37, "readMemory"), Arguments: ReadMemoryArguments{ MemoryReference: "someref", Count: 123, }, } var readMemoryResponseString = `{"seq":37,"type":"response","request_seq":38,"command":"readMemory","success":true,"body":{"address":"someaddr"}}` var readMemoryResponseStruct = ReadMemoryResponse{ Response: *newResponse(37, 38, "readMemory", true), Body: ReadMemoryResponseBody{Address: "someaddr"}, } // -------- Disassemble var disassembleRequestString = `{"seq":38,"type":"request","command":"disassemble","arguments":{"memoryReference":"someref","instructionCount":123}}` var disassembleRequestStruct = DisassembleRequest{ Request: *newRequest(38, "disassemble"), Arguments: DisassembleArguments{ MemoryReference: "someref", InstructionCount: 123, }, } var disassembleResponseString = `{"seq":38,"type":"response","request_seq":39,"command":"disassemble","success":true,"body":{"instructions":[{"address":"someaddr","instruction":"someinstr"}]}}` var disassembleResponseStruct = DisassembleResponse{ Response: *newResponse(38, 39, "disassemble", true), Body: DisassembleResponseBody{ Instructions: []DisassembledInstruction{ { Address: "someaddr", Instruction: "someinstr", }, }, }, } // -------- StartDebugging var startDebuggingRequestString = `{"seq":39,"type":"request","command":"startDebugging","arguments":{"request":"launch","configuration":{"any":true}}}` var startDebuggingRequestStruct = StartDebuggingRequest{ Request: *newRequest(39, "startDebugging"), Arguments: StartDebuggingRequestArguments{ Request: "launch", Configuration: map[string]interface{}{"any": true}, }, } var startDebuggingResponseString = `{"seq":39,"type":"response","request_seq":40,"command":"startDebugging","success":true}` var startDebuggingResponseStruct = StartDebuggingResponse{ Response: *newResponse(39, 40, "startDebugging", true), } // -------- Events -------- var initializedEventString = `{"seq":1,"type":"event","event":"initialized"}` var initializedEventStruct = InitializedEvent{ Event: *newEvent(1, "initialized"), } var stoppedEventString = `{"seq":2,"type":"event","event":"stopped","body":{"reason":"breakpoint","threadId":1,"allThreadsStopped":true}}` var stoppedEventStruct = StoppedEvent{ Event: *newEvent(2, "stopped"), Body: StoppedEventBody{Reason: "breakpoint", ThreadId: 1, AllThreadsStopped: true}, } var continuedEventString = `{"seq":3,"type":"event","event":"continued","body":{"threadId":123}}` var continuedEventStruct = ContinuedEvent{ Event: *newEvent(3, "continued"), Body: ContinuedEventBody{ThreadId: 123}, } var exitedEventString = `{"seq":4,"type":"event","event":"exited","body":{"exitCode":123}}` var exitedEventStruct = ExitedEvent{ Event: *newEvent(4, "exited"), Body: ExitedEventBody{ExitCode: 123}, } var terminatedEventString = `{"seq":5,"type":"event","event":"terminated","body":{"restart":true}}` var terminatedEventStruct = TerminatedEvent{ Event: *newEvent(5, "terminated"), Body: TerminatedEventBody{Restart: json.RawMessage(`true`)}, } var threadEventString = `{"seq":6,"type":"event","event":"thread","body":{"reason":"started","threadId":18}}` var threadEventStruct = ThreadEvent{ Event: *newEvent(6, "thread"), Body: ThreadEventBody{Reason: "started", ThreadId: 18}, } var outputEventString = `{"seq":7,"type":"event","event":"output","body":{"category":"stdout","output":"something that got logged"}}` var outputEventStruct = OutputEvent{ Event: *newEvent(7, "output"), Body: OutputEventBody{Category: "stdout", Output: "something that got logged"}, } var breakpointEventString = `{"seq":8,"type":"event","event":"breakpoint","body":{"reason":"new","breakpoint":{"verified":true}}}` var breakpointEventStruct = BreakpointEvent{ Event: *newEvent(8, "breakpoint"), Body: BreakpointEventBody{Reason: "new", Breakpoint: Breakpoint{Verified: true}}, } var moduleEventString = `{"seq":9,"type":"event","event":"module","body":{"reason":"removed","module":{"id":"id"}}}` var moduleEventStruct = ModuleEvent{ Event: *newEvent(9, "module"), Body: ModuleEventBody{Reason: "removed", Module: Module{Id: "id"}}, } var loadedSourceEventString = `{"seq":10,"type":"event","event":"loadedSource","body":{"reason":"changed","source":{"name":"hello.go","path":"/Users/foo/go/src/hello/hello.go"}}}` var loadedSourceEventStruct = LoadedSourceEvent{ Event: *newEvent(10, "loadedSource"), Body: LoadedSourceEventBody{Reason: "changed", Source: Source{Name: "hello.go", Path: "/Users/foo/go/src/hello/hello.go"}}, } var processEventString = `{"seq":11,"type":"event","event":"process","body":{"name":"/home/example/myproj/program.js"}}` var processEventStruct = ProcessEvent{ Event: *newEvent(11, "process"), Body: ProcessEventBody{Name: "/home/example/myproj/program.js"}, } var capabilitiesEventString = `{"seq":12,"type":"event","event":"capabilities","body":{"capabilities":{"supportsFunctionBreakpoints":true}}}` var capabilitiesEventStruct = CapabilitiesEvent{ Event: *newEvent(12, "capabilities"), Body: CapabilitiesEventBody{Capabilities: Capabilities{SupportsFunctionBreakpoints: true}}, } func TestDecodeProtocolMessage(t *testing.T) { // Sometimes partial messages can be returned on error, but // the user should not rely on those and just check err itself. // Hence the test will not check those. var msgIgnoredOnError Message const noError = "" tests := []struct { data string wantMsg Message wantErr string }{ // ProtocolMessage {``, msgIgnoredOnError, "unexpected end of JSON input"}, {`,`, msgIgnoredOnError, "invalid character ',' looking for beginning of value"}, {`{}`, msgIgnoredOnError, "ProtocolMessage type '' is not supported (seq: 0)"}, {`{"a": 1}`, msgIgnoredOnError, "ProtocolMessage type '' is not supported (seq: 0)"}, {`{"type":"foo", "seq": 2}`, msgIgnoredOnError, "ProtocolMessage type 'foo' is not supported (seq: 2)"}, // Request {`{"type":"request"}`, msgIgnoredOnError, "Request command '' is not supported (seq: 0)"}, {cancelRequestString, &cancelRequestStruct, noError}, {runInTerminalRequestString, &runInTerminalRequestStruct, noError}, {initializeRequestString, &initializeRequestStruct, noError}, {initializeRequestOmitDefaultsString, &initializeRequestStruct, noError}, {initializeRequestNotDefaultsString, &initializeRequestNotDefaultsStruct, noError}, {configurationDoneRequestString, &configurationDoneRequestStruct, noError}, {launchRequestString, &launchRequestStruct, noError}, {attachRequestString, &attachRequestStruct, noError}, {restartRequestString, &restartRequestStruct, noError}, {disconnectRequestString, &disconnectRequestStruct, noError}, {terminateRequestString, &terminateRequestStruct, noError}, {breakpointLocationsRequestString, &breakpointLocationsRequestStruct, noError}, {setBreakpointsRequestString, &setBreakpointsRequestStruct, noError}, {setFunctionBreakpointsRequestString, &setFunctionBreakpointsRequestStruct, noError}, {setExceptionBreakpointsRequestString, &setExceptionBreakpointsRequestStruct, noError}, {dataBreakpointInfoRequestString, &dataBreakpointInfoRequestStruct, noError}, {setDataBreakpointsRequestString, &setDataBreakpointsRequestStruct, noError}, {continueRequestString, &continueRequestStruct, noError}, {nextRequestString, &nextRequestStruct, noError}, {stepInRequestString, &stepInRequestStruct, noError}, {stepOutRequestString, &stepOutRequestStruct, noError}, {stepBackRequestString, &stepBackRequestStruct, noError}, {reverseContinueRequestString, &reverseContinueRequestStruct, noError}, {restartFrameRequestString, &restartFrameRequestStruct, noError}, {gotoRequestString, &gotoRequestStruct, noError}, {pauseRequestString, &pauseRequestStruct, noError}, {stackTraceRequestString, &stackTraceRequestStruct, noError}, {scopesRequestString, &scopesRequestStruct, noError}, {variablesRequestString, &variablesRequestStruct, noError}, {setVariableRequestString, &setVariableRequestStruct, noError}, {sourceRequestString, &sourceRequestStruct, noError}, {threadsRequestString, &threadsRequestStruct, noError}, {terminateThreadsRequestString, &terminateThreadsRequestStruct, noError}, {modulesRequestString, &modulesRequestStruct, noError}, {loadedSourcesRequestString, &loadedSourcesRequestStruct, noError}, {evaluateRequestString, &evaluateRequestStruct, noError}, {setExpressionRequestString, &setExpressionRequestStruct, noError}, {stepInTargetsRequestString, &stepInTargetsRequestStruct, noError}, {gotoTargetsRequestString, &gotoTargetsRequestStruct, noError}, {completionsRequestString, &completionsRequestStruct, noError}, {exceptionInfoRequestString, &exceptionInfoRequestStruct, noError}, {readMemoryRequestString, &readMemoryRequestStruct, noError}, {disassembleRequestString, &disassembleRequestStruct, noError}, {startDebuggingRequestString, &startDebuggingRequestStruct, noError}, // Response {`{"type":"response","success":true, "seq": 77}`, msgIgnoredOnError, "Response command '' is not supported (seq: 77)"}, {errorResponseString, &errorResponseStruct, noError}, {cancelResponseString, &cancelResponseStruct, noError}, {runInTerminalResponseString, &runInTerminalResponseStruct, noError}, {initializeResponseString, &initializeResponseStruct, noError}, {configurationDoneResponseString, &configurationDoneResponseStruct, noError}, {launchResponseString, &launchResponseStruct, noError}, {attachResponseString, &attachResponseStruct, noError}, {restartResponseString, &restartResponseStruct, noError}, {disconnectResponseString, &disconnectResponseStruct, noError}, {terminateResponseString, &terminateResponseStruct, noError}, {breakpointLocationsResponseString, &breakpointLocationsResponseStruct, noError}, {setBreakpointsResponseString, &setBreakpointsResponseStruct, noError}, {setFunctionBreakpointsResponseString, &setFunctionBreakpointsResponseStruct, noError}, {setExceptionBreakpointsResponseString, &setExceptionBreakpointsResponseStruct, noError}, {dataBreakpointInfoResponseString, &dataBreakpointInfoResponseStruct, noError}, {setDataBreakpointsResponseString, &setDataBreakpointsResponseStruct, noError}, {continueResponseString, &continueResponseStruct, noError}, {nextResponseString, &nextResponseStruct, noError}, {stepInResponseString, &stepInResponseStruct, noError}, {stepOutResponseString, &stepOutResponseStruct, noError}, {stepBackResponseString, &stepBackResponseStruct, noError}, {reverseContinueResponseString, &reverseContinueResponseStruct, noError}, {restartFrameResponseString, &restartFrameResponseStruct, noError}, {gotoResponseString, &gotoResponseStruct, noError}, {pauseResponseString, &pauseResponseStruct, noError}, {stackTraceResponseString, &stackTraceResponseStruct, noError}, {scopesResponseString, &scopesResponseStruct, noError}, {variablesResponseString, &variablesResponseStruct, noError}, {setVariableResponseString, &setVariableResponseStruct, noError}, {sourceResponseString, &sourceResponseStruct, noError}, {threadsResponseString, &threadsResponseStruct, noError}, {terminateThreadsResponseString, &terminateThreadsResponseStruct, noError}, {modulesResponseString, &modulesResponseStruct, noError}, {loadedSourcesResponseString, &loadedSourcesResponseStruct, noError}, {evaluateResponseString, &evaluateResponseStruct, noError}, {setExpressionResponseString, &setExpressionResponseStruct, noError}, {stepInTargetsResponseString, &stepInTargetsResponseStruct, noError}, {gotoTargetsResponseString, &gotoTargetsResponseStruct, noError}, {completionsResponseString, &completionsResponseStruct, noError}, {exceptionInfoResponseString, &exceptionInfoResponseStruct, noError}, {readMemoryResponseString, &readMemoryResponseStruct, noError}, {disassembleResponseString, &disassembleResponseStruct, noError}, {startDebuggingResponseString, &startDebuggingResponseStruct, noError}, // Event {`{"type":"event", "seq": 8}`, msgIgnoredOnError, "Event event '' is not supported (seq: 8)"}, {initializedEventString, &initializedEventStruct, noError}, {stoppedEventString, &stoppedEventStruct, noError}, {continuedEventString, &continuedEventStruct, noError}, {exitedEventString, &exitedEventStruct, noError}, {terminatedEventString, &terminatedEventStruct, noError}, {threadEventString, &threadEventStruct, noError}, {outputEventString, &outputEventStruct, noError}, {breakpointEventString, &breakpointEventStruct, noError}, {moduleEventString, &moduleEventStruct, noError}, {loadedSourceEventString, &loadedSourceEventStruct, noError}, {processEventString, &processEventStruct, noError}, {capabilitiesEventString, &capabilitiesEventStruct, noError}, } for _, test := range tests { t.Run(test.data, func(t *testing.T) { msg, err := DecodeProtocolMessage([]byte(test.data)) if err != nil { // Decoding error if err.Error() != test.wantErr { // Was it the right error? t.Errorf("got error=%#v, want %q", err, test.wantErr) } } else { // No decoding error if test.wantErr != "" { // Did we expect one? t.Errorf("got error=nil, want %#q", test.wantErr) } got, _ := json.Marshal(msg) want, _ := json.Marshal(test.wantMsg) if !reflect.DeepEqual(msg, test.wantMsg) { // Check result t.Errorf("\ngot message\n%s\nwant\n%s", got, want) } } }) } } // -------- Custom Request/Response and Event -------- type customRequest struct { Request Body string `json:"body"` } func (r *customRequest) GetRequest() *Request { return &r.Request } type customResponse struct { Response Body string `json:"body"` } func (r *customResponse) GetResponse() *Response { return &r.Response } var customRequestString = `{"seq":40,"type":"request","command":"customReq","body":"242424"}` var customRequestStruct = customRequest{ Request: *newRequest(40, "customReq"), Body: "242424", } var customResponseString = `{"seq":40,"type":"response","request_seq":41,"command":"customReq","success":true,"body":"424242"}` var customResponseStruct = customResponse{ Response: *newResponse(40, 41, "customReq", true), Body: "424242", } type customEvent struct { Event Body int `json:"body"` } func (e *customEvent) GetEvent() *Event { return &e.Event } var customEventString = `{"seq":13,"type":"event","event":"customEvt","body":42}` var customEventStruct = customEvent{ Event: *newEvent(13, "customEvt"), Body: 42, } func TestDecodeProtocolMessage_Custom(t *testing.T) { tests := []struct { data string wantMsg Message }{ {customRequestString, &customRequestStruct}, {customResponseString, &customResponseStruct}, {customEventString, &customEventStruct}, } codec := NewCodec() codec.RegisterRequest("customReq", func() Message { return new(customRequest) }, func() Message { return new(customResponse) }) codec.RegisterEvent("customEvt", func() Message { return new(customEvent) }) for _, test := range tests { t.Run(test.data, func(t *testing.T) { msg, err := codec.DecodeMessage([]byte(test.data)) if err != nil { // Decoding error t.Fatalf("codec.DecodeMessage() failed with %v", err) } got, _ := json.Marshal(msg) want, _ := json.Marshal(test.wantMsg) if !reflect.DeepEqual(msg, test.wantMsg) { // Check result t.Errorf("\ngot message\n%s\nwant\n%s", got, want) } }) } } // newRequest builds a Request struct with the specified fields. func newRequest(seq int, command string) *Request { return &Request{ ProtocolMessage: ProtocolMessage{ Type: "request", Seq: seq, }, Command: command, } } // newEvent builds an Event struct with the specified fields. func newEvent(seq int, event string) *Event { return &Event{ ProtocolMessage: ProtocolMessage{ Seq: seq, Type: "event", }, Event: event, } } // newResponse builds a Response struct with the specified fields. func newResponse(seq int, requestSeq int, command string, success bool) *Response { return &Response{ ProtocolMessage: ProtocolMessage{ Seq: seq, Type: "response", }, Command: command, RequestSeq: requestSeq, Success: success, } }