buffered-process-spec.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /* eslint-disable no-new */
  2. const ChildProcess = require('child_process');
  3. const path = require('path');
  4. const fs = require('fs-plus');
  5. const BufferedProcess = require('../src/buffered-process');
  6. describe('BufferedProcess', function() {
  7. describe('when a bad command is specified', function() {
  8. let [oldOnError] = [];
  9. beforeEach(function() {
  10. oldOnError = window.onerror;
  11. window.onerror = jasmine.createSpy();
  12. });
  13. afterEach(() => (window.onerror = oldOnError));
  14. describe('when there is an error handler specified', function() {
  15. describe('when an error event is emitted by the process', () =>
  16. it('calls the error handler and does not throw an exception', function() {
  17. const bufferedProcess = new BufferedProcess({
  18. command: 'bad-command-nope1',
  19. args: ['nothing'],
  20. options: { shell: false }
  21. });
  22. const errorSpy = jasmine
  23. .createSpy()
  24. .andCallFake(error => error.handle());
  25. bufferedProcess.onWillThrowError(errorSpy);
  26. waitsFor(() => errorSpy.callCount > 0);
  27. runs(function() {
  28. expect(window.onerror).not.toHaveBeenCalled();
  29. expect(errorSpy).toHaveBeenCalled();
  30. expect(errorSpy.mostRecentCall.args[0].error.message).toContain(
  31. 'spawn bad-command-nope1 ENOENT'
  32. );
  33. });
  34. }));
  35. describe('when an error is thrown spawning the process', () =>
  36. it('calls the error handler and does not throw an exception', function() {
  37. spyOn(ChildProcess, 'spawn').andCallFake(function() {
  38. const error = new Error('Something is really wrong');
  39. error.code = 'EAGAIN';
  40. throw error;
  41. });
  42. const bufferedProcess = new BufferedProcess({
  43. command: 'ls',
  44. args: [],
  45. options: {}
  46. });
  47. const errorSpy = jasmine
  48. .createSpy()
  49. .andCallFake(error => error.handle());
  50. bufferedProcess.onWillThrowError(errorSpy);
  51. waitsFor(() => errorSpy.callCount > 0);
  52. runs(function() {
  53. expect(window.onerror).not.toHaveBeenCalled();
  54. expect(errorSpy).toHaveBeenCalled();
  55. expect(errorSpy.mostRecentCall.args[0].error.message).toContain(
  56. 'Something is really wrong'
  57. );
  58. });
  59. }));
  60. });
  61. describe('when there is not an error handler specified', () =>
  62. it('does throw an exception', function() {
  63. new BufferedProcess({
  64. command: 'bad-command-nope2',
  65. args: ['nothing'],
  66. options: { shell: false }
  67. });
  68. waitsFor(() => window.onerror.callCount > 0);
  69. runs(function() {
  70. expect(window.onerror).toHaveBeenCalled();
  71. expect(window.onerror.mostRecentCall.args[0]).toContain(
  72. 'Failed to spawn command `bad-command-nope2`'
  73. );
  74. expect(window.onerror.mostRecentCall.args[4].name).toBe(
  75. 'BufferedProcessError'
  76. );
  77. });
  78. }));
  79. });
  80. describe('when autoStart is false', () =>
  81. /**
  82. * TODO: FAILING TEST - This test fails with the following output:
  83. * timeout: timed out after 120000 msec waiting for condition
  84. */
  85. xit('doesnt start unless start method is called', function() {
  86. let stdout = '';
  87. let stderr = '';
  88. const exitCallback = jasmine.createSpy('exit callback');
  89. const apmProcess = new BufferedProcess({
  90. autoStart: false,
  91. command: atom.packages.getApmPath(),
  92. args: ['-h'],
  93. options: {},
  94. stdout(lines) {
  95. stdout += lines;
  96. },
  97. stderr(lines) {
  98. stderr += lines;
  99. },
  100. exit: exitCallback
  101. });
  102. expect(apmProcess.started).not.toBe(true);
  103. apmProcess.start();
  104. expect(apmProcess.started).toBe(true);
  105. waitsFor(() => exitCallback.callCount === 1);
  106. runs(function() {
  107. expect(stderr).toContain('apm - Atom Package Manager');
  108. expect(stdout).toEqual('');
  109. });
  110. }));
  111. /**
  112. * TODO: FAILING TEST - This test fails with the following output:
  113. * timeout: timed out after 120000 msec waiting for condition
  114. */
  115. xit('calls the specified stdout, stderr, and exit callbacks', function() {
  116. let stdout = '';
  117. let stderr = '';
  118. const exitCallback = jasmine.createSpy('exit callback');
  119. new BufferedProcess({
  120. command: atom.packages.getApmPath(),
  121. args: ['-h'],
  122. options: {},
  123. stdout(lines) {
  124. stdout += lines;
  125. },
  126. stderr(lines) {
  127. stderr += lines;
  128. },
  129. exit: exitCallback
  130. });
  131. waitsFor(() => exitCallback.callCount === 1);
  132. runs(function() {
  133. expect(stderr).toContain('apm - Atom Package Manager');
  134. expect(stdout).toEqual('');
  135. });
  136. });
  137. it('calls the specified stdout callback with whole lines', function() {
  138. const exitCallback = jasmine.createSpy('exit callback');
  139. const loremPath = require.resolve('./fixtures/lorem.txt');
  140. const content = fs.readFileSync(loremPath).toString();
  141. let stdout = '';
  142. let allLinesEndWithNewline = true;
  143. new BufferedProcess({
  144. command: process.platform === 'win32' ? 'type' : 'cat',
  145. args: [loremPath],
  146. options: {},
  147. stdout(lines) {
  148. const endsWithNewline = lines.charAt(lines.length - 1) === '\n';
  149. if (!endsWithNewline) {
  150. allLinesEndWithNewline = false;
  151. }
  152. stdout += lines;
  153. },
  154. exit: exitCallback
  155. });
  156. waitsFor(() => exitCallback.callCount === 1);
  157. runs(function() {
  158. expect(allLinesEndWithNewline).toBe(true);
  159. expect(stdout).toBe(content);
  160. });
  161. });
  162. describe('on Windows', function() {
  163. let originalPlatform = null;
  164. beforeEach(function() {
  165. // Prevent any commands from actually running and affecting the host
  166. spyOn(ChildProcess, 'spawn');
  167. originalPlatform = process.platform;
  168. Object.defineProperty(process, 'platform', { value: 'win32' });
  169. });
  170. afterEach(() =>
  171. Object.defineProperty(process, 'platform', { value: originalPlatform })
  172. );
  173. describe('when the explorer command is spawned on Windows', () =>
  174. it("doesn't quote arguments of the form /root,C...", function() {
  175. new BufferedProcess({
  176. command: 'explorer.exe',
  177. args: ['/root,C:\\foo']
  178. });
  179. expect(ChildProcess.spawn.argsForCall[0][1][3]).toBe(
  180. '"explorer.exe /root,C:\\foo"'
  181. );
  182. }));
  183. it('spawns the command using a cmd.exe wrapper when options.shell is undefined', function() {
  184. new BufferedProcess({ command: 'dir' });
  185. expect(path.basename(ChildProcess.spawn.argsForCall[0][0])).toBe(
  186. 'cmd.exe'
  187. );
  188. expect(ChildProcess.spawn.argsForCall[0][1][0]).toBe('/s');
  189. expect(ChildProcess.spawn.argsForCall[0][1][1]).toBe('/d');
  190. expect(ChildProcess.spawn.argsForCall[0][1][2]).toBe('/c');
  191. expect(ChildProcess.spawn.argsForCall[0][1][3]).toBe('"dir"');
  192. });
  193. });
  194. });