diff --git a/packages/core/src/child-proxy/child-process-proxy.ts b/packages/core/src/child-proxy/child-process-proxy.ts index 3f40c85f60..e5d141d708 100644 --- a/packages/core/src/child-proxy/child-process-proxy.ts +++ b/packages/core/src/child-proxy/child-process-proxy.ts @@ -200,7 +200,7 @@ export default class ChildProcessProxy implements Disposable { this.reportError(this.currentError); function processOutOfMemory() { - return output.includes('JavaScript heap out of memory'); + return output.includes('JavaScript heap out of memory') || output.includes('FatalProcessOutOfMemory'); } function stdoutAndStderr() { diff --git a/packages/core/test/integration/child-proxy/child-process-proxy.it.spec.ts b/packages/core/test/integration/child-proxy/child-process-proxy.it.spec.ts index 4e0c195a58..2d1b62f178 100644 --- a/packages/core/test/integration/child-proxy/child-process-proxy.it.spec.ts +++ b/packages/core/test/integration/child-proxy/child-process-proxy.it.spec.ts @@ -32,6 +32,7 @@ describe(ChildProcessProxy.name, () => { log = currentLogMock(); sut = ChildProcessProxy.create(require.resolve('./echo'), { port, level: LogLevel.Debug }, options, { name: echoName }, workingDir, Echo, [ '--no-warnings', // test if node args are forwarded with this setting, see https://nodejs.org/api/cli.html#cli_no_warnings + '--max-old-space-size=32', // reduce the amount of time we have to wait on the OOM test ]); }); diff --git a/packages/core/test/unit/child-proxy/child-process-proxy.spec.ts b/packages/core/test/unit/child-proxy/child-process-proxy.spec.ts index 5eeec4bffa..86d93aa79b 100644 --- a/packages/core/test/unit/child-proxy/child-process-proxy.spec.ts +++ b/packages/core/test/unit/child-proxy/child-process-proxy.spec.ts @@ -21,6 +21,7 @@ import { import { LoggingClientContext } from '../../../src/logging'; import { serialize } from '../../../src/utils/object-utils'; import * as objectUtils from '../../../src/utils/object-utils'; +import OutOfMemoryError from '../../../src/child-proxy/out-of-memory-error'; import currentLogMock from '../../helpers/log-mock'; import { Mock } from '../../helpers/producers'; @@ -121,7 +122,7 @@ describe(ChildProcessProxy.name, () => { }); }); - describe('on close', () => { + describe('on unexpected close', () => { beforeEach(() => { sut = createSut(); }); @@ -164,6 +165,34 @@ describe(ChildProcessProxy.name, () => { return expect(sut.proxy.say('')).rejected; }); + it('should handle "JavaScript heap out of memory" as an OOM error', async () => { + // Arrange + childProcessMock.stderr.emit('data', 'some other output'); + childProcessMock.stderr.emit('data', ' JavaScript '); + childProcessMock.stderr.emit('data', 'heap out of memory'); + childProcessMock.stderr.emit('data', ' some more data"'); + + // Act + actClose(123); + + // Assert + await expect(sut.proxy.say('')).rejectedWith(OutOfMemoryError); + }); + + it('should handle "FatalProcessOutOfMemory" as an OOM error', async () => { + // Arrange + childProcessMock.stderr.emit( + 'data', + '3: 0xb797be v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/opt/hostedtoolcache/node/14.15.4/x64/bin/node]\n\t 4: 0xb79b37 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/opt/hostedtoolcache/node/14.15.4/x64/bin/node]\n\t 5: 0xd343c5 [/opt/hostedtoolcache/node/14.15.4/x64/bin/node]\n\t 6: 0xd34f4f [/opt/hostedtoolcache/node/14.15.4/x64/bin/node]\n\t 7: 0xd42fdb v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/opt/hostedtoolcache/node/14.15.4/x64/bin/node]\n\t 8: 0xd457ae v8::internal::Heap::CollectAllAvailableGarbage(v8::internal::GarbageCollectionReason) [/opt/hostedtoolcache/node/14.15.4/x64/bin/node]\n\t 9: 0xd46beb v8::internal::Heap::AllocateRawWithRetryOrFailSlowPath(int, v8::internal::AllocationType, v8::internal::AllocationOrigin, v8::internal::AllocationAlignment) [/opt/hostedtoolcache/node/14.15.4/x64/bin/node]\n\t10: 0xd0c4a2 v8::internal::Factory::AllocateRaw(int, v8::internal::AllocationType, v8::internal::AllocationAlignment) [/opt/hostedtoolcache/node/14.15.4/x64/bin/node]\n\t11: 0xd086f2 v8::internal::FactoryBase::AllocateRawArray(int, v8::internal::AllocationType) [/opt/hostedtoolcache/node/14.15.4/x64/bin/node]\n\t12: 0xd087a4 v8::internal::FactoryBase::NewFixedArrayWithFiller(v8::internal::Handle, int, v8::internal::Handle, v8::internal::AllocationType) [/opt/hostedtoolcache/node/14.15.4/x64/bin/node]\n\t13: 0xe9568e [/opt/hostedtoolcache/node/14.15.4/x64/bin/node]\n\t14: 0xe957da [/opt/hostedtoolcache/node/14.15.4/x64/bin/node]\n\t15: 0x1032b83 v8::internal::Runtime_GrowArrayElements(int, unsigned long*, v8::internal::Isolate*) [/opt/hostedtoolcache/node/14.15.4/x64/bin/node]\n\t16: 0x14011f9 [/opt/hostedtoolcache/node/14.15.4/x64/bin/node]\n\t' + ); + + // Act + actClose(123); + + // Assert + await expect(sut.proxy.say('')).rejectedWith(OutOfMemoryError); + }); + function actClose(code = 1, signal = 'SIGINT') { childProcessMock.emit('close', code, signal); }