-
Notifications
You must be signed in to change notification settings - Fork 425
Framework for debug adapter and mock ui #1088
Conversation
Your test plan is not reproducible. Ideally, another person should be able to get this pull request and verify that the test plan works on their setup. You should also strive get have automated tests from day 1. |
src/debugger/adapter/DebugAdapter.js
Outdated
// Start Prepack in a child process | ||
_startPrepack() { | ||
if (this._prepackCommand.length === 0) { | ||
console.error("No command given to start Prepack in adapter"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it OK to just carry one in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Terminated the adapter process in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also note that if no second argument was given, _prepackCommand will be undefined
.
if (arg.startsWith("--")) { | ||
arg = arg.slice(2); | ||
if (arg === "prepack") { | ||
this._prepackCommand = args.shift(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this work if you want to specify arguments to prepack itself?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the entire command is passed in as one argument. See example above. We can eventually make the command to be read from a file like the launch.json in vscode.
src/debugger/adapter/DebugAdapter.js
Outdated
}); | ||
|
||
this._prepackProcess.on("exit", () => { | ||
// Prepack is finished |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the adapter stay alive in this case?
src/debugger/adapter/DebugAdapter.js
Outdated
} | ||
|
||
/** | ||
* The 'initialize' request is the first request called by the frontend |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"frontend"? I assume that is the UI.
src/debugger/mock-ui/main.js
Outdated
import { Session } from "./session.js"; | ||
|
||
function run(process, console) { | ||
let adapterPath: string = ""; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need for a type annotation here. Flow is very good about inferring the types of local variables.
src/debugger/mock-ui/session.js
Outdated
// reset the content length to ensure it is extracted for the next message | ||
this._contentLength = -1; | ||
// process the message | ||
if (message.length > 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a comment to explain why the message could be length 0 would be helpful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should not be, I removed this check.
src/debugger/mock-ui/session.js
Outdated
} | ||
|
||
_processEvent(event: DebugProtocol.Event) { | ||
// to be implemented |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like you're not quite done with this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This and _processEvent will be implemented in the next PR to not make this one too big. For now, they just confirm a message is received and parsed by printing it.
src/debugger/mock-ui/session.js
Outdated
// they can be done without user input | ||
if (command === "init") { | ||
let args: DebugProtocol.InitializeRequestArguments = { | ||
clientID: "CLI", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a value prescribed by the protocol?
src/debugger/mock-ui/session.js
Outdated
if (command === "init") { | ||
let args: DebugProtocol.InitializeRequestArguments = { | ||
clientID: "CLI", | ||
adapterID: "Prepack", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clearly this is not prescribed by the protocol. It does seem a terse. Could there in the future be another adapter that involves Prepack?
src/debugger/mock-ui/session.js
Outdated
//separator for messages according to the protocol | ||
const TWO_CRLF = "\r\n\r\n"; | ||
|
||
export class Session { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not clear to me what a Session is and why it is distinct from the adapter. Mostly, I'm bemused by the logic that deals with parsing commands and buffering.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved the parsing and buffering into a separate class.
I will set up testing for the debugger in a separate PR afterwards to avoid making this one too big. |
src/debugger/adapter/DebugAdapter.js
Outdated
// Start Prepack in a child process | ||
_startPrepack() { | ||
if (this._prepackCommand.length === 0) { | ||
console.error("No command given to start Prepack in adapter"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also note that if no second argument was given, _prepackCommand will be undefined
.
//set up message queue | ||
this._messageQueue = new Queue(); | ||
|
||
this._prepackProcess = child_process.spawn("node", this._prepackCommand.split(" ")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spawn
or spawnSync
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need the asynchronous version here. The sychronous one will not return until the Prepack process has exited. But the adapter may need to run while Prepack is still running (e.g. passing state info to UI or passing new user commands to Prepack).
src/debugger/mock-ui/DataHandler.js
Outdated
for (let i = 0; i < lines.length; i++) { | ||
let pair = lines[i].split(/: +/); | ||
if (pair[0] === "Content-Length") { | ||
this._contentLength = +pair[1]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unary +
is confusing
src/debugger/mock-ui/DataHandler.js
Outdated
// if we know what length we are expecting | ||
if (this._contentLength >= 0) { | ||
// we have enough data to check for the expected message | ||
if (this._rawData.length >= this._contentLength) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Request Changes: Strictly speaking this just means that the buffer has allocated that much storage not necessarily that there are that many characters in the buffer. Please either make a comment or invariant around this if the behavior is intended.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to compare against actual length in the buffer.
let parts = input.split(" "); | ||
let command = parts[0]; | ||
|
||
// for testing purposes, init and configDone are made into user commands |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe pose this comment as a TODO
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the CLI, we can keep these two commands as being from user input. This way we can control tests on them using the CLI. In an actual IDE, these will be done without user input.
src/debugger/mock-ui/UISession.js
Outdated
|
||
// for testing purposes, init and configDone are made into user commands | ||
// they can be done from the adapter without user input | ||
if (command === "init") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once we get more commands, a switch statement is probably better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed.
src/debugger/mock-ui/UISession.js
Outdated
|
||
// tell the adapter that configuration is done so it can expect other commands | ||
_sendConfigDoneRequest(args: DebugProtocol.ConfigurationDoneArguments) { | ||
this._sequenceNum++; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could move the sequence num increment and stringify into the packageAndSend
to make sure you can't forget to increment the sequence num.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved the increment into _packageAndSend
. The stringify is harder to move because each request has a different type of arguments, so all the possible types would need to be specified in _packageAndSend
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, looks good to me from high level, I will let @mostafaeweda to take a further look.
this._invalidCount = 0; | ||
this._dataHandler = new DataHandler(); | ||
} | ||
// the parent (i.e. ui) process |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not the parent process, it is mock UI process itself.
process.exit(1); | ||
} | ||
//set up message queue | ||
this._messageQueue = new Queue(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this holding all the pending commands from UI? Did not see any code using it. Will be used in later PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this will be used in the next PR when all 3 parts (DebugServer in Prepack, DebugAdapter, UI) are connected.
// Filename aliases | ||
declare module 'vscode-debugprotocol/lib/debugProtocol.js' { | ||
declare module.exports: $Exports<'vscode-debugprotocol/lib/debugProtocol'>; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file doesn't seem very useful (it's mostly all any
) & I can't find it in https://github.com/flowtype/flow-typed -- which should be the source of truth for any npm module types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file was autogenerated using the CLI from https://github.com/flowtype/flow-typed because it's not there already. It is needed to satisfy flow checks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the file doesn't add any value, you should just add it to the ignore
section in .flowconfig
.
* of patent rights can be found in the PATENTS file in the same directory. | ||
*/ | ||
|
||
/* @flow */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I see this project is setup with prettier
in the devDependencies
- but don't see the @format
--> did you use prettier to format this file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our prettier seems to run off of directory structure instead of @Format annotations, everything matching src/**/*.js
or scripts/**/*.js
should get formatted.
src/debugger/adapter/DebugAdapter.js
Outdated
this._prepackProcess = child_process.spawn("node", this._prepackCommand.split(" ")); | ||
|
||
process.on("exit", () => { | ||
if (this._prepackProcess) this._prepackProcess.kill(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this._prepackProcess
is never set to null
--> so, no need to check for null
-- OR (change the flow type to nullable and set to null
here)
src/debugger/adapter/DebugAdapter.js
Outdated
|
||
response.body = response.body || {}; | ||
response.body.supportsConfigurationDoneRequest = true; | ||
//Respond back to the UI with the configurations. Will add more configurations gradually as needed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit space after //
break; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is duplicating Nuclide's V8Protocol.js
& VsDebugSession.js
(https://github.com/facebook/nuclide/blob/master/pkg/nuclide-debugger-common/lib/V8Protocol.js)
Also, there's the npm package: vscode-debugadapter-testsupport
which'd help you setup and send individual events to the adapter in a promise-based fashion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would be the cleanest way to reuse those files and keep them in sync between the two repos?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed offline that it'd be best to switch to using the vscode npm package.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will use the npm package to set up automated testing.
this._adapterProcess.kill(); | ||
this._proc.exit(0); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, this is all duplicated work
|
||
/* @flow */ | ||
|
||
import { UISession } from "./UISession.js"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should better wrap Nuclide's VsDebugSession
or use VSCode's utils instead.
Summary: - Mock UI as a CLI to communicate with the adapter - Starter code for the debugger adapter with an initialize request Test: - Ran the mock-ui, can send the initialize request and receive a response on the CLI
- fix check for buffer size - change if to switch for commands
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@JWZ2018 is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
Release note: none
Issue: #907
Summary:
Test:
node lib/debugger/mock-ui/debugger-cli.js --adapterPath <path_to_debug_adapter> --prepack <command_to_start_prepack>
e.g.
node lib/debugger/mock-ui/debugger-cli.js --adapterPath lib/debugger/adapter/DebugAdapter.js --prepack "lib/prepack-cli.js test/serializer/basic/Date.js"
init <clientID> <adapterID>
: sends an initialize request to the adapter and receives a response printed to consoleconfigDone
: sends a configurationDone request to the adapter and receives a response printed to consoleexit
: quit the UINote: for testing purposes,
init
andconfigDone
are made into user commands. These can be done without user input.