node-pty 0.11.0-beta9 → 1.1.0-beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +5 -4
  2. package/binding.gyp +70 -29
  3. package/lib/conpty_console_list_agent.js.map +1 -1
  4. package/lib/terminal.js +10 -4
  5. package/lib/terminal.js.map +1 -1
  6. package/lib/terminal.test.js +3 -0
  7. package/lib/terminal.test.js.map +1 -1
  8. package/lib/unixTerminal.js +30 -4
  9. package/lib/unixTerminal.js.map +1 -1
  10. package/lib/unixTerminal.test.js +147 -18
  11. package/lib/unixTerminal.test.js.map +1 -1
  12. package/lib/windowsPtyAgent.js +13 -6
  13. package/lib/windowsPtyAgent.js.map +1 -1
  14. package/lib/windowsTerminal.js +21 -3
  15. package/lib/windowsTerminal.js.map +1 -1
  16. package/lib/windowsTerminal.test.js +98 -97
  17. package/lib/windowsTerminal.test.js.map +1 -1
  18. package/lib/worker/conoutSocketWorker.js +3 -0
  19. package/lib/worker/conoutSocketWorker.js.map +1 -1
  20. package/package.json +3 -4
  21. package/scripts/post-install.js +1 -0
  22. package/src/conpty_console_list_agent.ts +1 -1
  23. package/src/interfaces.ts +8 -2
  24. package/src/native.d.ts +6 -5
  25. package/src/terminal.test.ts +3 -0
  26. package/src/terminal.ts +21 -20
  27. package/src/tsconfig.json +1 -3
  28. package/src/unix/pty.cc +297 -205
  29. package/src/unix/spawn-helper.cc +23 -0
  30. package/src/unixTerminal.test.ts +117 -25
  31. package/src/unixTerminal.ts +31 -11
  32. package/src/win/conpty.cc +85 -38
  33. package/src/win/conpty_console_list.cc +3 -3
  34. package/src/win/path_util.cc +22 -3
  35. package/src/win/path_util.h +1 -0
  36. package/src/win/winpty.cc +118 -54
  37. package/src/windowsConoutConnection.ts +1 -1
  38. package/src/windowsPtyAgent.ts +16 -11
  39. package/src/windowsTerminal.test.ts +100 -99
  40. package/src/windowsTerminal.ts +23 -4
  41. package/src/worker/conoutSocketWorker.ts +4 -0
  42. package/typings/node-pty.d.ts +11 -20
  43. package/deps/winpty/.gitignore +0 -15
  44. package/deps/winpty/misc/.gitignore +0 -2
  45. package/scripts/install.js +0 -18
package/src/win/winpty.cc CHANGED
@@ -9,6 +9,7 @@
9
9
  */
10
10
 
11
11
  #include <iostream>
12
+ #include <map>
12
13
  #include <nan.h>
13
14
  #include <Shlwapi.h> // PathCombine, PathIsRelative
14
15
  #include <sstream>
@@ -37,21 +38,25 @@ static volatile LONG ptyCounter;
37
38
  * Helpers
38
39
  */
39
40
 
40
- static winpty_t *get_pipe_handle(int handle) {
41
+ /** Keeps track of the handles created by PtyStartProcess */
42
+ static std::map<DWORD, HANDLE> createdHandles;
43
+
44
+ static winpty_t *get_pipe_handle(DWORD pid) {
41
45
  for (size_t i = 0; i < ptyHandles.size(); ++i) {
42
46
  winpty_t *ptyHandle = ptyHandles[i];
43
- int current = (int)winpty_agent_process(ptyHandle);
44
- if (current == handle) {
47
+ HANDLE current = winpty_agent_process(ptyHandle);
48
+ if (GetProcessId(current) == pid) {
45
49
  return ptyHandle;
46
50
  }
47
51
  }
48
52
  return nullptr;
49
53
  }
50
54
 
51
- static bool remove_pipe_handle(int handle) {
55
+ static bool remove_pipe_handle(DWORD pid) {
52
56
  for (size_t i = 0; i < ptyHandles.size(); ++i) {
53
57
  winpty_t *ptyHandle = ptyHandles[i];
54
- if ((int)winpty_agent_process(ptyHandle) == handle) {
58
+ HANDLE current = winpty_agent_process(ptyHandle);
59
+ if (GetProcessId(current) == pid) {
55
60
  winpty_free(ptyHandle);
56
61
  ptyHandles.erase(ptyHandles.begin() + i);
57
62
  ptyHandle = nullptr;
@@ -62,11 +67,10 @@ static bool remove_pipe_handle(int handle) {
62
67
  }
63
68
 
64
69
  void throw_winpty_error(const char *generalMsg, winpty_error_ptr_t error_ptr) {
65
- std::stringstream why;
70
+ std::wstringstream why;
66
71
  std::wstring msg(winpty_error_msg(error_ptr));
67
- std::string msg_(msg.begin(), msg.end());
68
- why << generalMsg << ": " << msg_;
69
- Nan::ThrowError(why.str().c_str());
72
+ why << generalMsg << ": " << msg;
73
+ Nan::ThrowError(path_util::from_wstring(why.str().c_str()));
70
74
  winpty_error_free(error_ptr);
71
75
  }
72
76
 
@@ -75,13 +79,24 @@ static NAN_METHOD(PtyGetExitCode) {
75
79
 
76
80
  if (info.Length() != 1 ||
77
81
  !info[0]->IsNumber()) {
78
- Nan::ThrowError("Usage: pty.getExitCode(pidHandle)");
82
+ Nan::ThrowError("Usage: pty.getExitCode(pid)");
83
+ return;
84
+ }
85
+
86
+ DWORD pid = info[0]->Uint32Value(Nan::GetCurrentContext()).FromJust();
87
+ HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
88
+ if (handle == NULL) {
89
+ info.GetReturnValue().Set(Nan::New<v8::Number>(-1));
79
90
  return;
80
91
  }
81
92
 
82
93
  DWORD exitCode = 0;
83
- GetExitCodeProcess((HANDLE)info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust(), &exitCode);
94
+ BOOL success = GetExitCodeProcess(handle, &exitCode);
95
+ if (success == FALSE) {
96
+ exitCode = -1;
97
+ }
84
98
 
99
+ CloseHandle(handle);
85
100
  info.GetReturnValue().Set(Nan::New<v8::Number>(exitCode));
86
101
  }
87
102
 
@@ -94,8 +109,7 @@ static NAN_METHOD(PtyGetProcessList) {
94
109
  return;
95
110
  }
96
111
 
97
- int pid = info[0]->Int32Value(Nan::GetCurrentContext()).FromJust();
98
-
112
+ DWORD pid = info[0]->Uint32Value(Nan::GetCurrentContext()).FromJust();
99
113
  winpty_t *pc = get_pipe_handle(pid);
100
114
  if (pc == nullptr) {
101
115
  info.GetReturnValue().Set(Nan::New<v8::Array>(0));
@@ -104,9 +118,13 @@ static NAN_METHOD(PtyGetProcessList) {
104
118
  int processList[64];
105
119
  const int processCount = 64;
106
120
  int actualCount = winpty_get_console_process_list(pc, processList, processCount, nullptr);
107
-
108
- v8::Local<v8::Array> result = Nan::New<v8::Array>(actualCount);
109
- for (uint32_t i = 0; i < actualCount; i++) {
121
+ if (actualCount <= 0) {
122
+ info.GetReturnValue().Set(Nan::New<v8::Array>(0));
123
+ return;
124
+ }
125
+ uint32_t actualCountSize = static_cast<uint32_t>(actualCount);
126
+ v8::Local<v8::Array> result = Nan::New<v8::Array>(actualCountSize);
127
+ for (uint32_t i = 0; i < actualCountSize; i++) {
110
128
  Nan::Set(result, i, Nan::New<v8::Number>(processList[i]));
111
129
  }
112
130
  info.GetReturnValue().Set(result);
@@ -127,8 +145,6 @@ static NAN_METHOD(PtyStartProcess) {
127
145
  return;
128
146
  }
129
147
 
130
- std::stringstream why;
131
-
132
148
  const wchar_t *filename = path_util::to_wstring(Nan::Utf8String(info[0]));
133
149
  const wchar_t *cmdline = path_util::to_wstring(Nan::Utf8String(info[1]));
134
150
  const wchar_t *cwd = path_util::to_wstring(Nan::Utf8String(info[3]));
@@ -157,12 +173,14 @@ static NAN_METHOD(PtyStartProcess) {
157
173
  shellpath = filename;
158
174
  }
159
175
 
160
- std::string shellpath_(shellpath.begin(), shellpath.end());
161
-
162
176
  if (shellpath.empty() || !path_util::file_exists(shellpath)) {
163
- why << "File not found: " << shellpath_;
164
- Nan::ThrowError(why.str().c_str());
165
- goto cleanup;
177
+ std::wstringstream why;
178
+ why << "File not found: " << shellpath;
179
+ Nan::ThrowError(path_util::from_wstring(why.str().c_str()));
180
+ delete filename;
181
+ delete cmdline;
182
+ delete cwd;
183
+ return;
166
184
  }
167
185
 
168
186
  int cols = info[4]->Int32Value(Nan::GetCurrentContext()).FromJust();
@@ -177,7 +195,10 @@ static NAN_METHOD(PtyStartProcess) {
177
195
  winpty_config_t* winpty_config = winpty_config_new(0, &error_ptr);
178
196
  if (winpty_config == nullptr) {
179
197
  throw_winpty_error("Error creating WinPTY config", error_ptr);
180
- goto cleanup;
198
+ delete filename;
199
+ delete cmdline;
200
+ delete cwd;
201
+ return;
181
202
  }
182
203
  winpty_error_free(error_ptr);
183
204
 
@@ -189,18 +210,22 @@ static NAN_METHOD(PtyStartProcess) {
189
210
  winpty_config_free(winpty_config);
190
211
  if (pc == nullptr) {
191
212
  throw_winpty_error("Error launching WinPTY agent", error_ptr);
192
- goto cleanup;
213
+ delete filename;
214
+ delete cmdline;
215
+ delete cwd;
216
+ return;
193
217
  }
194
218
  winpty_error_free(error_ptr);
195
219
 
196
- // Save pty struct for later use
197
- ptyHandles.insert(ptyHandles.end(), pc);
198
-
199
220
  // Create winpty spawn config
200
221
  winpty_spawn_config_t* config = winpty_spawn_config_new(WINPTY_SPAWN_FLAG_AUTO_SHUTDOWN, shellpath.c_str(), cmdline, cwd, env.c_str(), &error_ptr);
201
222
  if (config == nullptr) {
202
223
  throw_winpty_error("Error creating WinPTY spawn config", error_ptr);
203
- goto cleanup;
224
+ winpty_free(pc);
225
+ delete filename;
226
+ delete cmdline;
227
+ delete cwd;
228
+ return;
204
229
  }
205
230
  winpty_error_free(error_ptr);
206
231
 
@@ -210,32 +235,68 @@ static NAN_METHOD(PtyStartProcess) {
210
235
  winpty_spawn_config_free(config);
211
236
  if (!spawnSuccess) {
212
237
  throw_winpty_error("Unable to start terminal process", error_ptr);
213
- goto cleanup;
238
+ if (handle) {
239
+ CloseHandle(handle);
240
+ }
241
+ winpty_free(pc);
242
+ delete filename;
243
+ delete cmdline;
244
+ delete cwd;
245
+ return;
214
246
  }
215
247
  winpty_error_free(error_ptr);
216
248
 
217
- // Set return values
249
+ LPCWSTR coninPipeName = winpty_conin_name(pc);
250
+ std::string coninPipeNameStr(path_util::from_wstring(coninPipeName));
251
+ if (coninPipeNameStr.empty()) {
252
+ Nan::ThrowError("Failed to initialize winpty conin");
253
+ CloseHandle(handle);
254
+ winpty_free(pc);
255
+ delete filename;
256
+ delete cmdline;
257
+ delete cwd;
258
+ return;
259
+ }
260
+
261
+ LPCWSTR conoutPipeName = winpty_conout_name(pc);
262
+ std::string conoutPipeNameStr(path_util::from_wstring(conoutPipeName));
263
+ if (conoutPipeNameStr.empty()) {
264
+ Nan::ThrowError("Failed to initialize winpty conout");
265
+ CloseHandle(handle);
266
+ winpty_free(pc);
267
+ delete filename;
268
+ delete cmdline;
269
+ delete cwd;
270
+ return;
271
+ }
272
+
273
+ DWORD innerPid = GetProcessId(handle);
274
+ if (createdHandles[innerPid]) {
275
+ std::stringstream why;
276
+ why << "There is already a process with innerPid " << innerPid;
277
+ Nan::ThrowError(why.str().c_str());
278
+ CloseHandle(handle);
279
+ winpty_free(pc);
280
+ delete filename;
281
+ delete cmdline;
282
+ delete cwd;
283
+ return;
284
+ }
285
+ createdHandles[innerPid] = handle;
286
+
287
+ // Save pty struct for later use
288
+ ptyHandles.push_back(pc);
289
+
218
290
  v8::Local<v8::Object> marshal = Nan::New<v8::Object>();
219
- Nan::Set(marshal, Nan::New<v8::String>("innerPid").ToLocalChecked(), Nan::New<v8::Number>((int)GetProcessId(handle)));
220
- Nan::Set(marshal, Nan::New<v8::String>("innerPidHandle").ToLocalChecked(), Nan::New<v8::Number>((int)handle));
221
- Nan::Set(marshal, Nan::New<v8::String>("pid").ToLocalChecked(), Nan::New<v8::Number>((int)winpty_agent_process(pc)));
291
+ DWORD pid = GetProcessId(winpty_agent_process(pc));
292
+ Nan::Set(marshal, Nan::New<v8::String>("innerPid").ToLocalChecked(), Nan::New<v8::Number>(static_cast<double>(innerPid)));
293
+ Nan::Set(marshal, Nan::New<v8::String>("pid").ToLocalChecked(), Nan::New<v8::Number>(static_cast<double>(pid)));
222
294
  Nan::Set(marshal, Nan::New<v8::String>("pty").ToLocalChecked(), Nan::New<v8::Number>(InterlockedIncrement(&ptyCounter)));
223
295
  Nan::Set(marshal, Nan::New<v8::String>("fd").ToLocalChecked(), Nan::New<v8::Number>(-1));
224
- {
225
- LPCWSTR coninPipeName = winpty_conin_name(pc);
226
- std::wstring coninPipeNameWStr(coninPipeName);
227
- std::string coninPipeNameStr(coninPipeNameWStr.begin(), coninPipeNameWStr.end());
228
- Nan::Set(marshal, Nan::New<v8::String>("conin").ToLocalChecked(), Nan::New<v8::String>(coninPipeNameStr).ToLocalChecked());
229
- LPCWSTR conoutPipeName = winpty_conout_name(pc);
230
- std::wstring conoutPipeNameWStr(conoutPipeName);
231
- std::string conoutPipeNameStr(conoutPipeNameWStr.begin(), conoutPipeNameWStr.end());
232
- Nan::Set(marshal, Nan::New<v8::String>("conout").ToLocalChecked(), Nan::New<v8::String>(conoutPipeNameStr).ToLocalChecked());
233
- }
296
+ Nan::Set(marshal, Nan::New<v8::String>("conin").ToLocalChecked(), Nan::New<v8::String>(coninPipeNameStr).ToLocalChecked());
297
+ Nan::Set(marshal, Nan::New<v8::String>("conout").ToLocalChecked(), Nan::New<v8::String>(conoutPipeNameStr).ToLocalChecked());
234
298
  info.GetReturnValue().Set(marshal);
235
299
 
236
- goto cleanup;
237
-
238
- cleanup:
239
300
  delete filename;
240
301
  delete cmdline;
241
302
  delete cwd;
@@ -252,11 +313,11 @@ static NAN_METHOD(PtyResize) {
252
313
  return;
253
314
  }
254
315
 
255
- int handle = info[0]->Int32Value(Nan::GetCurrentContext()).FromJust();
316
+ DWORD pid = info[0]->Uint32Value(Nan::GetCurrentContext()).FromJust();
256
317
  int cols = info[1]->Int32Value(Nan::GetCurrentContext()).FromJust();
257
318
  int rows = info[2]->Int32Value(Nan::GetCurrentContext()).FromJust();
258
319
 
259
- winpty_t *pc = get_pipe_handle(handle);
320
+ winpty_t *pc = get_pipe_handle(pid);
260
321
 
261
322
  if (pc == nullptr) {
262
323
  Nan::ThrowError("The pty doesn't appear to exist");
@@ -277,20 +338,23 @@ static NAN_METHOD(PtyKill) {
277
338
  if (info.Length() != 2 ||
278
339
  !info[0]->IsNumber() ||
279
340
  !info[1]->IsNumber()) {
280
- Nan::ThrowError("Usage: pty.kill(pid, innerPidHandle)");
341
+ Nan::ThrowError("Usage: pty.kill(pid, innerPid)");
281
342
  return;
282
343
  }
283
344
 
284
- int handle = info[0]->Int32Value(Nan::GetCurrentContext()).FromJust();
285
- HANDLE innerPidHandle = (HANDLE)info[1]->Int32Value(Nan::GetCurrentContext()).FromJust();
345
+ DWORD pid = info[0]->Uint32Value(Nan::GetCurrentContext()).FromJust();
346
+ DWORD innerPid = info[1]->Uint32Value(Nan::GetCurrentContext()).FromJust();
286
347
 
287
- winpty_t *pc = get_pipe_handle(handle);
348
+ winpty_t *pc = get_pipe_handle(pid);
288
349
  if (pc == nullptr) {
289
350
  Nan::ThrowError("Pty seems to have been killed already");
290
351
  return;
291
352
  }
292
353
 
293
- assert(remove_pipe_handle(handle));
354
+ assert(remove_pipe_handle(pid));
355
+
356
+ HANDLE innerPidHandle = createdHandles[innerPid];
357
+ createdHandles.erase(innerPid);
294
358
  CloseHandle(innerPidHandle);
295
359
 
296
360
  return info.GetReturnValue().SetUndefined();
@@ -30,7 +30,7 @@ const FLUSH_DATA_INTERVAL = 1000;
30
30
  */
31
31
  export class ConoutConnection implements IDisposable {
32
32
  private _worker: Worker;
33
- private _drainTimeout: NodeJS.Timeout;
33
+ private _drainTimeout: NodeJS.Timeout | undefined;
34
34
  private _isDisposed: boolean = false;
35
35
 
36
36
  private _onReady = new EventEmitter2<void>();
@@ -29,10 +29,9 @@ const FLUSH_DATA_INTERVAL = 1000;
29
29
  export class WindowsPtyAgent {
30
30
  private _inSocket: Socket;
31
31
  private _outSocket: Socket;
32
- private _pid: number;
33
- private _innerPid: number;
34
- private _innerPidHandle: number;
35
- private _closeTimeout: NodeJS.Timer;
32
+ private _pid: number = 0;
33
+ private _innerPid: number = 0;
34
+ private _closeTimeout: NodeJS.Timer | undefined;
36
35
  private _exitCode: number | undefined;
37
36
  private _conoutSocketWorker: ConoutConnection;
38
37
 
@@ -105,7 +104,6 @@ export class WindowsPtyAgent {
105
104
  term = (this._ptyNative as IWinptyNative).startProcess(file, commandLine, env, cwd, cols, rows, debug);
106
105
  this._pid = (term as IWinptyProcess).pid;
107
106
  this._innerPid = (term as IWinptyProcess).innerPid;
108
- this._innerPidHandle = (term as IWinptyProcess).innerPidHandle;
109
107
  }
110
108
 
111
109
  // Not available on windows.
@@ -152,6 +150,12 @@ export class WindowsPtyAgent {
152
150
  this._ptyNative.resize(this._pid, cols, rows);
153
151
  }
154
152
 
153
+ public clear(): void {
154
+ if (this._useConpty) {
155
+ (this._ptyNative as IConptyNative).clear(this._pty);
156
+ }
157
+ }
158
+
155
159
  public kill(): void {
156
160
  this._inSocket.readable = false;
157
161
  this._outSocket.readable = false;
@@ -168,14 +172,14 @@ export class WindowsPtyAgent {
168
172
  (this._ptyNative as IConptyNative).kill(this._pty);
169
173
  });
170
174
  } else {
171
- (this._ptyNative as IWinptyNative).kill(this._pid, this._innerPidHandle);
172
- // Since pty.kill closes the handle it will kill most processes by itself
173
- // and process IDs can be reused as soon as all handles to them are
174
- // dropped, we want to immediately kill the entire console process list.
175
+ // Because pty.kill closes the handle, it will kill most processes by itself.
176
+ // Process IDs can be reused as soon as all handles to them are
177
+ // dropped, so we want to immediately kill the entire console process list.
175
178
  // If we do not force kill all processes here, node servers in particular
176
179
  // seem to become detached and remain running (see
177
180
  // Microsoft/vscode#26807).
178
181
  const processList: number[] = (this._ptyNative as IWinptyNative).getProcessList(this._pid);
182
+ (this._ptyNative as IWinptyNative).kill(this._pid, this._innerPid);
179
183
  processList.forEach(pid => {
180
184
  try {
181
185
  process.kill(pid);
@@ -202,11 +206,12 @@ export class WindowsPtyAgent {
202
206
  });
203
207
  }
204
208
 
205
- public get exitCode(): number {
209
+ public get exitCode(): number | undefined {
206
210
  if (this._useConpty) {
207
211
  return this._exitCode;
208
212
  }
209
- return (this._ptyNative as IWinptyNative).getExitCode(this._innerPidHandle);
213
+ const winptyExitCode = (this._ptyNative as IWinptyNative).getExitCode(this._innerPid);
214
+ return winptyExitCode === -1 ? undefined : winptyExitCode;
210
215
  }
211
216
 
212
217
  private _getWindowsBuildNumber(): number {
@@ -65,7 +65,7 @@ function pollForProcessTreeSize(pid: number, size: number, intervalMs: number =
65
65
  })[0]);
66
66
  const list: IWindowsProcessTreeResult[] = [];
67
67
  while (openList.length) {
68
- const current = openList.shift();
68
+ const current = openList.shift()!;
69
69
  ps.filter(p => p.ppid === current.pid).map(p => {
70
70
  return { name: p.name, pid: p.pid };
71
71
  }).forEach(p => openList.push(p));
@@ -81,7 +81,6 @@ function pollForProcessTreeSize(pid: number, size: number, intervalMs: number =
81
81
  if (tries * intervalMs >= timeoutMs) {
82
82
  clearInterval(interval);
83
83
  assert.fail(`Bad process state, expected: ${size}, actual: ${list.length}`);
84
- resolve();
85
84
  }
86
85
  });
87
86
  }, intervalMs);
@@ -89,125 +88,127 @@ function pollForProcessTreeSize(pid: number, size: number, intervalMs: number =
89
88
  }
90
89
 
91
90
  if (process.platform === 'win32') {
92
- describe('WindowsTerminal', () => {
93
- describe('kill', () => {
94
- it('should not crash parent process', (done) => {
95
- const term = new WindowsTerminal('cmd.exe', [], {});
96
- term.kill();
97
- // Add done call to deferred function queue to ensure the kill call has completed
98
- (<any>term)._defer(done);
99
- });
100
- it('should kill the process tree', function (done: Mocha.Done): void {
101
- this.timeout(5000);
102
- const term = new WindowsTerminal('cmd.exe', [], {});
103
- // Start sub-processes
104
- term.write('powershell.exe\r');
105
- term.write('notepad.exe\r');
106
- term.write('node.exe\r');
107
- pollForProcessTreeSize(term.pid, 4, 500, 5000).then(list => {
108
- assert.equal(list[0].name, 'cmd.exe');
109
- assert.equal(list[1].name, 'powershell.exe');
110
- assert.equal(list[2].name, 'notepad.exe');
111
- assert.equal(list[3].name, 'node.exe');
91
+ [true, false].forEach((useConpty) => {
92
+ describe(`WindowsTerminal (useConpty = ${useConpty})`, () => {
93
+ describe('kill', () => {
94
+ it('should not crash parent process', (done) => {
95
+ const term = new WindowsTerminal('cmd.exe', [], { useConpty });
112
96
  term.kill();
113
- const desiredState: IProcessState = {};
114
- desiredState[list[0].pid] = false;
115
- desiredState[list[1].pid] = false;
116
- desiredState[list[2].pid] = true;
117
- desiredState[list[3].pid] = false;
118
- pollForProcessState(desiredState).then(() => {
119
- // Kill notepad before done
120
- process.kill(list[2].pid);
121
- done();
97
+ // Add done call to deferred function queue to ensure the kill call has completed
98
+ (<any>term)._defer(done);
99
+ });
100
+ it('should kill the process tree', function (done: Mocha.Done): void {
101
+ this.timeout(5000);
102
+ const term = new WindowsTerminal('cmd.exe', [], { useConpty });
103
+ // Start sub-processes
104
+ term.write('powershell.exe\r');
105
+ term.write('notepad.exe\r');
106
+ term.write('node.exe\r');
107
+ pollForProcessTreeSize(term.pid, 4, 500, 5000).then(list => {
108
+ assert.strictEqual(list[0].name.toLowerCase(), 'cmd.exe');
109
+ assert.strictEqual(list[1].name.toLowerCase(), 'powershell.exe');
110
+ assert.strictEqual(list[2].name.toLowerCase(), 'notepad.exe');
111
+ assert.strictEqual(list[3].name.toLowerCase(), 'node.exe');
112
+ term.kill();
113
+ const desiredState: IProcessState = {};
114
+ desiredState[list[0].pid] = false;
115
+ desiredState[list[1].pid] = false;
116
+ desiredState[list[2].pid] = true;
117
+ desiredState[list[3].pid] = false;
118
+ pollForProcessState(desiredState).then(() => {
119
+ // Kill notepad before done
120
+ process.kill(list[2].pid);
121
+ done();
122
+ });
122
123
  });
123
124
  });
124
125
  });
125
- });
126
126
 
127
- describe('resize', () => {
128
- it('should throw a non-native exception when resizing an invalid value', () => {
129
- const term = new WindowsTerminal('cmd.exe', [], {});
130
- assert.throws(() => term.resize(-1, -1));
131
- assert.throws(() => term.resize(0, 0));
132
- assert.doesNotThrow(() => term.resize(1, 1));
133
- });
134
- it('should throw an non-native exception when resizing a killed terminal', (done) => {
135
- const term = new WindowsTerminal('cmd.exe', [], {});
136
- (<any>term)._defer(() => {
137
- term.on('exit', () => {
138
- assert.throws(() => term.resize(1, 1));
139
- done();
127
+ describe('resize', () => {
128
+ it('should throw a non-native exception when resizing an invalid value', () => {
129
+ const term = new WindowsTerminal('cmd.exe', [], { useConpty });
130
+ assert.throws(() => term.resize(-1, -1));
131
+ assert.throws(() => term.resize(0, 0));
132
+ assert.doesNotThrow(() => term.resize(1, 1));
133
+ });
134
+ it('should throw a non-native exception when resizing a killed terminal', (done) => {
135
+ const term = new WindowsTerminal('cmd.exe', [], { useConpty });
136
+ (<any>term)._defer(() => {
137
+ term.on('exit', () => {
138
+ assert.throws(() => term.resize(1, 1));
139
+ done();
140
+ });
141
+ term.destroy();
140
142
  });
141
- term.destroy();
142
143
  });
143
144
  });
144
- });
145
145
 
146
- describe('Args as CommandLine', () => {
147
- it('should not fail running a file containing a space in the path', (done) => {
148
- const spaceFolder = path.resolve(__dirname, '..', 'fixtures', 'space folder');
149
- if (!fs.existsSync(spaceFolder)) {
150
- fs.mkdirSync(spaceFolder);
151
- }
146
+ describe('Args as CommandLine', () => {
147
+ it('should not fail running a file containing a space in the path', (done) => {
148
+ const spaceFolder = path.resolve(__dirname, '..', 'fixtures', 'space folder');
149
+ if (!fs.existsSync(spaceFolder)) {
150
+ fs.mkdirSync(spaceFolder);
151
+ }
152
152
 
153
- const cmdCopiedPath = path.resolve(spaceFolder, 'cmd.exe');
154
- const data = fs.readFileSync(`${process.env.windir}\\System32\\cmd.exe`);
155
- fs.writeFileSync(cmdCopiedPath, data);
153
+ const cmdCopiedPath = path.resolve(spaceFolder, 'cmd.exe');
154
+ const data = fs.readFileSync(`${process.env.windir}\\System32\\cmd.exe`);
155
+ fs.writeFileSync(cmdCopiedPath, data);
156
156
 
157
- if (!fs.existsSync(cmdCopiedPath)) {
158
- // Skip test if git bash isn't installed
159
- return;
160
- }
161
- const term = new WindowsTerminal(cmdCopiedPath, '/c echo "hello world"', {});
162
- let result = '';
163
- term.on('data', (data) => {
164
- result += data;
165
- });
166
- term.on('exit', () => {
167
- assert.ok(result.indexOf('hello world') >= 1);
168
- done();
157
+ if (!fs.existsSync(cmdCopiedPath)) {
158
+ // Skip test if git bash isn't installed
159
+ return;
160
+ }
161
+ const term = new WindowsTerminal(cmdCopiedPath, '/c echo "hello world"', { useConpty });
162
+ let result = '';
163
+ term.on('data', (data) => {
164
+ result += data;
165
+ });
166
+ term.on('exit', () => {
167
+ assert.ok(result.indexOf('hello world') >= 1);
168
+ done();
169
+ });
169
170
  });
170
171
  });
171
- });
172
172
 
173
- describe('env', () => {
174
- it('should set environment variables of the shell', (done) => {
175
- const term = new WindowsTerminal('cmd.exe', '/C echo %FOO%', { env: { FOO: 'BAR' }});
176
- let result = '';
177
- term.on('data', (data) => {
178
- result += data;
179
- });
180
- term.on('exit', () => {
181
- assert.ok(result.indexOf('BAR') >= 0);
182
- done();
173
+ describe('env', () => {
174
+ it('should set environment variables of the shell', (done) => {
175
+ const term = new WindowsTerminal('cmd.exe', '/C echo %FOO%', { env: { FOO: 'BAR' }});
176
+ let result = '';
177
+ term.on('data', (data) => {
178
+ result += data;
179
+ });
180
+ term.on('exit', () => {
181
+ assert.ok(result.indexOf('BAR') >= 0);
182
+ done();
183
+ });
183
184
  });
184
185
  });
185
- });
186
186
 
187
- describe('On close', () => {
188
- it('should return process zero exit codes', (done) => {
189
- const term = new WindowsTerminal('cmd.exe', '/C exit');
190
- term.on('exit', (code) => {
191
- assert.equal(code, 0);
192
- done();
187
+ describe('On close', () => {
188
+ it('should return process zero exit codes', (done) => {
189
+ const term = new WindowsTerminal('cmd.exe', '/C exit', { useConpty });
190
+ term.on('exit', (code) => {
191
+ assert.strictEqual(code, 0);
192
+ done();
193
+ });
193
194
  });
194
- });
195
195
 
196
- it('should return process non-zero exit codes', (done) => {
197
- const term = new WindowsTerminal('cmd.exe', '/C exit 2');
198
- term.on('exit', (code) => {
199
- assert.equal(code, 2);
200
- done();
196
+ it('should return process non-zero exit codes', (done) => {
197
+ const term = new WindowsTerminal('cmd.exe', '/C exit 2', { useConpty });
198
+ term.on('exit', (code) => {
199
+ assert.strictEqual(code, 2);
200
+ done();
201
+ });
201
202
  });
202
203
  });
203
- });
204
204
 
205
- describe('winpty', () => {
206
- it('should accept input', (done) => {
207
- const term = new WindowsTerminal('cmd.exe', '', { useConpty: false });
208
- term.write('exit\r');
209
- term.on('exit', () => {
210
- done();
205
+ describe('Write', () => {
206
+ it('should accept input', (done) => {
207
+ const term = new WindowsTerminal('cmd.exe', '', { useConpty });
208
+ term.write('exit\r');
209
+ term.on('exit', () => {
210
+ done();
211
+ });
211
212
  });
212
213
  });
213
214
  });