2019-08-29 11:47:54 -04:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
|
|
|
/// <reference types="node" />
|
|
|
|
|
|
|
|
import * as cluster from 'cluster';
|
|
|
|
|
2020-03-04 07:35:52 -05:00
|
|
|
import {MockFileSystemNative} from '../../../../src/ngtsc/file_system/testing';
|
2019-08-29 11:47:54 -04:00
|
|
|
import {ClusterExecutor} from '../../../src/execution/cluster/executor';
|
|
|
|
import {ClusterMaster} from '../../../src/execution/cluster/master';
|
|
|
|
import {ClusterWorker} from '../../../src/execution/cluster/worker';
|
2020-03-04 07:35:52 -05:00
|
|
|
import {AsyncLocker} from '../../../src/execution/lock_file';
|
2019-08-29 11:47:54 -04:00
|
|
|
import {PackageJsonUpdater} from '../../../src/writing/package_json_updater';
|
2020-03-04 07:35:52 -05:00
|
|
|
import {MockLockFile} from '../../helpers/mock_lock_file';
|
2019-08-29 11:47:54 -04:00
|
|
|
import {MockLogger} from '../../helpers/mock_logger';
|
|
|
|
import {mockProperty} from '../../helpers/spy_utils';
|
|
|
|
|
|
|
|
|
|
|
|
describe('ClusterExecutor', () => {
|
|
|
|
const runAsClusterMaster = mockProperty(cluster, 'isMaster');
|
|
|
|
let masterRunSpy: jasmine.Spy;
|
|
|
|
let workerRunSpy: jasmine.Spy;
|
|
|
|
let mockLogger: MockLogger;
|
2020-03-04 07:35:52 -05:00
|
|
|
let lockFileLog: string[];
|
|
|
|
let mockLockFile: MockLockFile;
|
|
|
|
let locker: AsyncLocker;
|
2019-08-29 11:47:54 -04:00
|
|
|
let executor: ClusterExecutor;
|
|
|
|
|
|
|
|
beforeEach(() => {
|
2020-01-10 04:54:58 -05:00
|
|
|
masterRunSpy = spyOn(ClusterMaster.prototype, 'run')
|
|
|
|
.and.returnValue(Promise.resolve('CusterMaster#run()'));
|
|
|
|
workerRunSpy = spyOn(ClusterWorker.prototype, 'run')
|
|
|
|
.and.returnValue(Promise.resolve('CusterWorker#run()'));
|
2019-08-29 11:47:54 -04:00
|
|
|
|
|
|
|
mockLogger = new MockLogger();
|
2020-03-04 07:35:52 -05:00
|
|
|
lockFileLog = [];
|
|
|
|
mockLockFile = new MockLockFile(new MockFileSystemNative(), lockFileLog);
|
|
|
|
locker = new AsyncLocker(mockLockFile, mockLogger, 200, 2);
|
|
|
|
executor = new ClusterExecutor(42, mockLogger, null as unknown as PackageJsonUpdater, locker);
|
2019-08-29 11:47:54 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('execute()', () => {
|
|
|
|
describe('(on cluster master)', () => {
|
|
|
|
beforeEach(() => runAsClusterMaster(true));
|
|
|
|
|
2020-02-03 15:25:15 -05:00
|
|
|
it('should log debug info about the executor', async() => {
|
2019-08-29 11:47:54 -04:00
|
|
|
const anyFn: () => any = () => undefined;
|
2020-02-03 15:25:15 -05:00
|
|
|
await executor.execute(anyFn, anyFn);
|
2019-08-29 11:47:54 -04:00
|
|
|
|
|
|
|
expect(mockLogger.logs.debug).toEqual([
|
|
|
|
['Running ngcc on ClusterExecutor (using 42 worker processes).'],
|
|
|
|
]);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should delegate to `ClusterMaster#run()`', async() => {
|
|
|
|
const analyzeEntryPointsSpy = jasmine.createSpy('analyzeEntryPoints');
|
|
|
|
const createCompilerFnSpy = jasmine.createSpy('createCompilerFn');
|
|
|
|
|
|
|
|
expect(await executor.execute(analyzeEntryPointsSpy, createCompilerFnSpy))
|
|
|
|
.toBe('CusterMaster#run()' as any);
|
|
|
|
|
|
|
|
expect(masterRunSpy).toHaveBeenCalledWith();
|
|
|
|
expect(workerRunSpy).not.toHaveBeenCalled();
|
|
|
|
|
|
|
|
expect(analyzeEntryPointsSpy).toHaveBeenCalledWith();
|
|
|
|
expect(createCompilerFnSpy).not.toHaveBeenCalled();
|
|
|
|
});
|
2020-01-10 04:54:58 -05:00
|
|
|
|
2020-03-04 07:35:52 -05:00
|
|
|
it('should call LockFile.write() and LockFile.remove() if master runner completes successfully',
|
2020-01-10 04:54:58 -05:00
|
|
|
async() => {
|
|
|
|
const anyFn: () => any = () => undefined;
|
|
|
|
await executor.execute(anyFn, anyFn);
|
2020-03-04 07:35:52 -05:00
|
|
|
expect(lockFileLog).toEqual(['write()', 'remove()']);
|
2020-01-10 04:54:58 -05:00
|
|
|
});
|
|
|
|
|
2020-03-04 07:35:52 -05:00
|
|
|
it('should call LockFile.write() and LockFile.remove() if master runner fails', async() => {
|
2020-01-10 04:54:58 -05:00
|
|
|
const anyFn: () => any = () => undefined;
|
|
|
|
masterRunSpy.and.returnValue(Promise.reject(new Error('master runner error')));
|
|
|
|
let error = '';
|
|
|
|
try {
|
|
|
|
await executor.execute(anyFn, anyFn);
|
|
|
|
} catch (e) {
|
|
|
|
error = e.message;
|
|
|
|
}
|
|
|
|
expect(error).toEqual('master runner error');
|
2020-03-04 07:35:52 -05:00
|
|
|
expect(lockFileLog).toEqual(['write()', 'remove()']);
|
2020-01-10 04:54:58 -05:00
|
|
|
});
|
|
|
|
|
2020-03-04 07:35:52 -05:00
|
|
|
it('should not call master runner if LockFile.write() fails', async() => {
|
2020-01-10 04:54:58 -05:00
|
|
|
const anyFn: () => any = () => undefined;
|
2020-03-04 07:35:52 -05:00
|
|
|
spyOn(mockLockFile, 'write').and.callFake(() => {
|
|
|
|
lockFileLog.push('write()');
|
|
|
|
throw new Error('LockFile.write() error');
|
|
|
|
});
|
|
|
|
|
2020-01-10 04:54:58 -05:00
|
|
|
executor =
|
2020-03-04 07:35:52 -05:00
|
|
|
new ClusterExecutor(42, mockLogger, null as unknown as PackageJsonUpdater, locker);
|
2020-01-10 04:54:58 -05:00
|
|
|
let error = '';
|
|
|
|
try {
|
|
|
|
await executor.execute(anyFn, anyFn);
|
|
|
|
} catch (e) {
|
|
|
|
error = e.message;
|
|
|
|
}
|
2020-03-04 07:35:52 -05:00
|
|
|
expect(error).toEqual('LockFile.write() error');
|
2020-01-10 04:54:58 -05:00
|
|
|
expect(masterRunSpy).not.toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
|
2020-03-04 07:35:52 -05:00
|
|
|
it('should fail if LockFile.remove() fails', async() => {
|
2020-01-10 04:54:58 -05:00
|
|
|
const anyFn: () => any = () => undefined;
|
2020-03-04 07:35:52 -05:00
|
|
|
spyOn(mockLockFile, 'remove').and.callFake(() => {
|
|
|
|
lockFileLog.push('remove()');
|
|
|
|
throw new Error('LockFile.remove() error');
|
|
|
|
});
|
|
|
|
|
2020-01-10 04:54:58 -05:00
|
|
|
executor =
|
2020-03-04 07:35:52 -05:00
|
|
|
new ClusterExecutor(42, mockLogger, null as unknown as PackageJsonUpdater, locker);
|
2020-01-10 04:54:58 -05:00
|
|
|
let error = '';
|
|
|
|
try {
|
|
|
|
await executor.execute(anyFn, anyFn);
|
|
|
|
} catch (e) {
|
|
|
|
error = e.message;
|
|
|
|
}
|
|
|
|
expect(error).toEqual('LockFile.remove() error');
|
2020-03-04 07:35:52 -05:00
|
|
|
expect(lockFileLog).toEqual(['write()', 'remove()']);
|
2020-01-10 04:54:58 -05:00
|
|
|
expect(masterRunSpy).toHaveBeenCalled();
|
|
|
|
});
|
2019-08-29 11:47:54 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
describe('(on cluster worker)', () => {
|
|
|
|
beforeEach(() => runAsClusterMaster(false));
|
|
|
|
|
2020-02-03 15:25:15 -05:00
|
|
|
it('should not log debug info about the executor', async() => {
|
2019-08-29 11:47:54 -04:00
|
|
|
const anyFn: () => any = () => undefined;
|
2020-02-03 15:25:15 -05:00
|
|
|
await executor.execute(anyFn, anyFn);
|
2019-08-29 11:47:54 -04:00
|
|
|
|
|
|
|
expect(mockLogger.logs.debug).toEqual([]);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should delegate to `ClusterWorker#run()`', async() => {
|
|
|
|
const analyzeEntryPointsSpy = jasmine.createSpy('analyzeEntryPoints');
|
|
|
|
const createCompilerFnSpy = jasmine.createSpy('createCompilerFn');
|
|
|
|
|
|
|
|
expect(await executor.execute(analyzeEntryPointsSpy, createCompilerFnSpy))
|
|
|
|
.toBe('CusterWorker#run()' as any);
|
|
|
|
|
|
|
|
expect(masterRunSpy).not.toHaveBeenCalledWith();
|
|
|
|
expect(workerRunSpy).toHaveBeenCalled();
|
|
|
|
|
|
|
|
expect(analyzeEntryPointsSpy).not.toHaveBeenCalled();
|
|
|
|
expect(createCompilerFnSpy).toHaveBeenCalledWith(jasmine.any(Function));
|
|
|
|
});
|
2020-01-10 04:54:58 -05:00
|
|
|
|
2020-03-04 07:35:52 -05:00
|
|
|
it('should not call LockFile.write() or LockFile.remove()', async() => {
|
2020-01-10 04:54:58 -05:00
|
|
|
const anyFn: () => any = () => undefined;
|
|
|
|
await executor.execute(anyFn, anyFn);
|
2020-03-04 07:35:52 -05:00
|
|
|
expect(lockFileLog).toEqual([]);
|
2020-01-10 04:54:58 -05:00
|
|
|
});
|
2019-08-29 11:47:54 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|