feat(ngcc): pause async ngcc processing if another process has the lockfile (#35131)
ngcc uses a lockfile to prevent two ngcc instances from executing at the same time. Previously, if a lockfile was found the current process would error and exit. Now, when in async mode, the current process is able to wait for the previous process to release the lockfile before continuing itself. PR Close #35131
This commit is contained in:

committed by
Alex Rickabaugh

parent
7e8ce24116
commit
eef07539a6
@ -14,7 +14,7 @@ import {ClusterExecutor} from '../../../src/execution/cluster/executor';
|
||||
import {ClusterMaster} from '../../../src/execution/cluster/master';
|
||||
import {ClusterWorker} from '../../../src/execution/cluster/worker';
|
||||
import {PackageJsonUpdater} from '../../../src/writing/package_json_updater';
|
||||
import {MockLockFile} from '../../helpers/mock_lock_file';
|
||||
import {MockLockFileAsync} from '../../helpers/mock_lock_file';
|
||||
import {MockLogger} from '../../helpers/mock_logger';
|
||||
import {mockProperty} from '../../helpers/spy_utils';
|
||||
|
||||
@ -24,7 +24,7 @@ describe('ClusterExecutor', () => {
|
||||
let masterRunSpy: jasmine.Spy;
|
||||
let workerRunSpy: jasmine.Spy;
|
||||
let mockLogger: MockLogger;
|
||||
let mockLockFile: MockLockFile;
|
||||
let mockLockFile: MockLockFileAsync;
|
||||
let executor: ClusterExecutor;
|
||||
|
||||
beforeEach(() => {
|
||||
@ -34,7 +34,7 @@ describe('ClusterExecutor', () => {
|
||||
.and.returnValue(Promise.resolve('CusterWorker#run()'));
|
||||
|
||||
mockLogger = new MockLogger();
|
||||
mockLockFile = new MockLockFile();
|
||||
mockLockFile = new MockLockFileAsync();
|
||||
executor =
|
||||
new ClusterExecutor(42, mockLogger, null as unknown as PackageJsonUpdater, mockLockFile);
|
||||
});
|
||||
@ -43,9 +43,9 @@ describe('ClusterExecutor', () => {
|
||||
describe('(on cluster master)', () => {
|
||||
beforeEach(() => runAsClusterMaster(true));
|
||||
|
||||
it('should log debug info about the executor', () => {
|
||||
it('should log debug info about the executor', async() => {
|
||||
const anyFn: () => any = () => undefined;
|
||||
executor.execute(anyFn, anyFn);
|
||||
await executor.execute(anyFn, anyFn);
|
||||
|
||||
expect(mockLogger.logs.debug).toEqual([
|
||||
['Running ngcc on ClusterExecutor (using 42 worker processes).'],
|
||||
@ -88,7 +88,7 @@ describe('ClusterExecutor', () => {
|
||||
|
||||
it('should not call master runner if Lockfile.create() fails', async() => {
|
||||
const anyFn: () => any = () => undefined;
|
||||
const lockFile = new MockLockFile({throwOnCreate: true});
|
||||
const lockFile = new MockLockFileAsync({throwOnCreate: true});
|
||||
executor =
|
||||
new ClusterExecutor(42, mockLogger, null as unknown as PackageJsonUpdater, lockFile);
|
||||
let error = '';
|
||||
@ -104,7 +104,7 @@ describe('ClusterExecutor', () => {
|
||||
|
||||
it('should fail if Lockfile.remove() fails', async() => {
|
||||
const anyFn: () => any = () => undefined;
|
||||
const lockFile = new MockLockFile({throwOnRemove: true});
|
||||
const lockFile = new MockLockFileAsync({throwOnRemove: true});
|
||||
executor =
|
||||
new ClusterExecutor(42, mockLogger, null as unknown as PackageJsonUpdater, lockFile);
|
||||
let error = '';
|
||||
@ -122,9 +122,9 @@ describe('ClusterExecutor', () => {
|
||||
describe('(on cluster worker)', () => {
|
||||
beforeEach(() => runAsClusterMaster(false));
|
||||
|
||||
it('should not log debug info about the executor', () => {
|
||||
it('should not log debug info about the executor', async() => {
|
||||
const anyFn: () => any = () => undefined;
|
||||
executor.execute(anyFn, anyFn);
|
||||
await executor.execute(anyFn, anyFn);
|
||||
|
||||
expect(mockLogger.logs.debug).toEqual([]);
|
||||
});
|
||||
|
@ -6,44 +6,130 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as process from 'process';
|
||||
import {FileSystem, getFileSystem} from '../../../src/ngtsc/file_system';
|
||||
import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
||||
import {LockFile} from '../../src/execution/lock_file';
|
||||
|
||||
/**
|
||||
* This class allows us to test the protected methods of LockFile directly,
|
||||
* which are normally hidden as "protected".
|
||||
*
|
||||
* We also add logging in here to track what is being called and in what order.
|
||||
*
|
||||
* Finally this class stubs out the `exit()` method to prevent unit tests from exiting the process.
|
||||
*/
|
||||
class LockFileUnderTest extends LockFile {
|
||||
log: string[] = [];
|
||||
constructor(fs: FileSystem, private handleSignals = false) {
|
||||
super(fs);
|
||||
fs.ensureDir(fs.dirname(this.lockFilePath));
|
||||
}
|
||||
create() {
|
||||
this.log.push('create()');
|
||||
super.create();
|
||||
}
|
||||
remove() {
|
||||
this.log.push('remove()');
|
||||
super.remove();
|
||||
}
|
||||
addSignalHandlers() {
|
||||
if (this.handleSignals) {
|
||||
super.addSignalHandlers();
|
||||
}
|
||||
}
|
||||
removeSignalHandlers() { super.removeSignalHandlers(); }
|
||||
exit(code: number) { this.log.push(`exit(${code})`); }
|
||||
}
|
||||
import {CachedFileSystem, FileSystem, getFileSystem} from '../../../src/ngtsc/file_system';
|
||||
import {runInEachFileSystem} from '../../../src/ngtsc/file_system/testing';
|
||||
import {LockFileAsync, LockFileBase, LockFileSync} from '../../src/execution/lock_file';
|
||||
import {MockLogger} from '../helpers/mock_logger';
|
||||
|
||||
runInEachFileSystem(() => {
|
||||
describe('LockFile', () => {
|
||||
describe('lock() - synchronous', () => {
|
||||
describe('LockFileBase', () => {
|
||||
/**
|
||||
* This class allows us to test the abstract class LockFileBase.
|
||||
*/
|
||||
class LockFileUnderTest extends LockFileBase {
|
||||
log: string[] = [];
|
||||
constructor(fs: FileSystem, private handleSignals = false) {
|
||||
super(fs);
|
||||
fs.ensureDir(fs.dirname(this.lockFilePath));
|
||||
}
|
||||
remove() { super.remove(); }
|
||||
addSignalHandlers() {
|
||||
this.log.push('addSignalHandlers()');
|
||||
if (this.handleSignals) {
|
||||
super.addSignalHandlers();
|
||||
}
|
||||
}
|
||||
writeLockFile() { super.writeLockFile(); }
|
||||
readLockFile() { return super.readLockFile(); }
|
||||
removeSignalHandlers() {
|
||||
this.log.push('removeSignalHandlers()');
|
||||
super.removeSignalHandlers();
|
||||
}
|
||||
exit(code: number) { this.log.push(`exit(${code})`); }
|
||||
}
|
||||
|
||||
describe('writeLockFile()', () => {
|
||||
it('should call `addSignalHandlers()`', () => {
|
||||
const fs = getFileSystem();
|
||||
const lockFile = new LockFileUnderTest(fs);
|
||||
lockFile.writeLockFile();
|
||||
expect(lockFile.log).toEqual(['addSignalHandlers()']);
|
||||
});
|
||||
|
||||
it('should call `removeSignalHandlers()` if there is an error', () => {
|
||||
const fs = getFileSystem();
|
||||
spyOn(fs, 'writeFile').and.throwError('WRITING ERROR');
|
||||
const lockFile = new LockFileUnderTest(fs);
|
||||
expect(() => lockFile.writeLockFile()).toThrowError('WRITING ERROR');
|
||||
expect(lockFile.log).toEqual(['addSignalHandlers()', 'removeSignalHandlers()']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('readLockFile()', () => {
|
||||
it('should return the contents of the lockfile', () => {
|
||||
const fs = getFileSystem();
|
||||
const lockFile = new LockFileUnderTest(fs);
|
||||
fs.writeFile(lockFile.lockFilePath, '188');
|
||||
expect(lockFile.readLockFile()).toEqual('188');
|
||||
});
|
||||
|
||||
it('should return `{unknown}` if the lockfile does not exist', () => {
|
||||
const fs = getFileSystem();
|
||||
const lockFile = new LockFileUnderTest(fs);
|
||||
expect(lockFile.readLockFile()).toEqual('{unknown}');
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove()', () => {
|
||||
it('should remove the lock file from the file-system', () => {
|
||||
const fs = getFileSystem();
|
||||
const lockFile = new LockFileUnderTest(fs);
|
||||
fs.writeFile(lockFile.lockFilePath, '188');
|
||||
lockFile.remove();
|
||||
expect(fs.exists(lockFile.lockFilePath)).toBe(false);
|
||||
});
|
||||
|
||||
it('should not error if the lock file does not exist', () => {
|
||||
const fs = getFileSystem();
|
||||
const lockFile = new LockFileUnderTest(fs);
|
||||
expect(() => lockFile.remove()).not.toThrow();
|
||||
});
|
||||
|
||||
it('should call removeSignalHandlers()', () => {
|
||||
const fs = getFileSystem();
|
||||
const lockFile = new LockFileUnderTest(fs);
|
||||
fs.writeFile(lockFile.lockFilePath, '188');
|
||||
lockFile.remove();
|
||||
expect(lockFile.log).toEqual(['removeSignalHandlers()']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('LockFileSync', () => {
|
||||
/**
|
||||
* This class allows us to test the protected methods of LockFileSync directly,
|
||||
* which are normally hidden as "protected".
|
||||
*
|
||||
* We also add logging in here to track what is being called and in what order.
|
||||
*
|
||||
* Finally this class stubs out the `exit()` method to prevent unit tests from exiting the
|
||||
* process.
|
||||
*/
|
||||
class LockFileUnderTest extends LockFileSync {
|
||||
log: string[] = [];
|
||||
constructor(fs: FileSystem, private handleSignals = false) {
|
||||
super(fs);
|
||||
fs.ensureDir(fs.dirname(this.lockFilePath));
|
||||
}
|
||||
create() {
|
||||
this.log.push('create()');
|
||||
super.create();
|
||||
}
|
||||
remove() {
|
||||
this.log.push('remove()');
|
||||
super.remove();
|
||||
}
|
||||
addSignalHandlers() {
|
||||
if (this.handleSignals) {
|
||||
super.addSignalHandlers();
|
||||
}
|
||||
}
|
||||
removeSignalHandlers() { super.removeSignalHandlers(); }
|
||||
exit(code: number) { this.log.push(`exit(${code})`); }
|
||||
}
|
||||
|
||||
describe('lock()', () => {
|
||||
it('should guard the `fn()` with calls to `create()` and `remove()`', () => {
|
||||
const fs = getFileSystem();
|
||||
const lockFile = new LockFileUnderTest(fs);
|
||||
@ -101,72 +187,6 @@ runInEachFileSystem(() => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('lock() - asynchronous', () => {
|
||||
it('should guard the `fn()` with calls to `create()` and `remove()`', async() => {
|
||||
const fs = getFileSystem();
|
||||
const lockFile = new LockFileUnderTest(fs);
|
||||
|
||||
await lockFile.lock(async() => {
|
||||
lockFile.log.push('fn() - before');
|
||||
// This promise forces node to do a tick in this function, ensuring that we are truly
|
||||
// testing an async scenario.
|
||||
await Promise.resolve();
|
||||
lockFile.log.push('fn() - after');
|
||||
});
|
||||
expect(lockFile.log).toEqual(['create()', 'fn() - before', 'fn() - after', 'remove()']);
|
||||
});
|
||||
|
||||
it('should guard the `fn()` with calls to `create()` and `remove()`, even if it throws',
|
||||
async() => {
|
||||
let error: string = '';
|
||||
const fs = getFileSystem();
|
||||
const lockFile = new LockFileUnderTest(fs);
|
||||
lockFile.create = () => lockFile.log.push('create()');
|
||||
lockFile.remove = () => lockFile.log.push('remove()');
|
||||
|
||||
try {
|
||||
await lockFile.lock(async() => {
|
||||
lockFile.log.push('fn()');
|
||||
throw new Error('ERROR');
|
||||
});
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
}
|
||||
expect(error).toEqual('ERROR');
|
||||
expect(lockFile.log).toEqual(['create()', 'fn()', 'remove()']);
|
||||
});
|
||||
|
||||
it('should remove the lockfile if CTRL-C is triggered', async() => {
|
||||
const fs = getFileSystem();
|
||||
const lockFile = new LockFileUnderTest(fs, /* handleSignals */ true);
|
||||
|
||||
await lockFile.lock(async() => {
|
||||
lockFile.log.push('SIGINT');
|
||||
process.emit('SIGINT', 'SIGINT');
|
||||
});
|
||||
// Since the test does not actually exit process, the `remove()` is called one more time.
|
||||
expect(lockFile.log).toEqual(['create()', 'SIGINT', 'remove()', 'exit(1)', 'remove()']);
|
||||
// Clean up the signal handlers. In practice this is not needed since the process would have
|
||||
// been terminated already.
|
||||
lockFile.removeSignalHandlers();
|
||||
});
|
||||
|
||||
it('should remove the lockfile if terminal is closed', async() => {
|
||||
const fs = getFileSystem();
|
||||
const lockFile = new LockFileUnderTest(fs, /* handleSignals */ true);
|
||||
|
||||
await lockFile.lock(async() => {
|
||||
lockFile.log.push('SIGHUP');
|
||||
process.emit('SIGHUP', 'SIGHUP');
|
||||
});
|
||||
// Since this does not actually exit process, the `remove()` is called one more time.
|
||||
expect(lockFile.log).toEqual(['create()', 'SIGHUP', 'remove()', 'exit(1)', 'remove()']);
|
||||
// Clean up the signal handlers. In practice this is not needed since the process would have
|
||||
// been terminated already.
|
||||
lockFile.removeSignalHandlers();
|
||||
});
|
||||
});
|
||||
|
||||
describe('create()', () => {
|
||||
it('should write a lock file to the file-system', () => {
|
||||
const fs = getFileSystem();
|
||||
@ -188,21 +208,180 @@ runInEachFileSystem(() => {
|
||||
`(If you are sure no ngcc process is running then you should delete the lockfile at ${lockFile.lockFilePath}.)`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('remove()', () => {
|
||||
it('should remove the lock file from the file-system', () => {
|
||||
describe('LockFileAsync', () => {
|
||||
/**
|
||||
* This class allows us to test the protected methods of LockFileAsync directly,
|
||||
* which are normally hidden as "protected".
|
||||
*
|
||||
* We also add logging in here to track what is being called and in what order.
|
||||
*
|
||||
* Finally this class stubs out the `exit()` method to prevent unit tests from exiting the
|
||||
* process.
|
||||
*/
|
||||
class LockFileUnderTest extends LockFileAsync {
|
||||
log: string[] = [];
|
||||
constructor(
|
||||
fs: FileSystem, retryDelay = 100, retryAttempts = 10, private handleSignals = false) {
|
||||
super(fs, new MockLogger(), retryDelay, retryAttempts);
|
||||
fs.ensureDir(fs.dirname(this.lockFilePath));
|
||||
}
|
||||
async create() {
|
||||
this.log.push('create()');
|
||||
await super.create();
|
||||
}
|
||||
remove() {
|
||||
this.log.push('remove()');
|
||||
super.remove();
|
||||
}
|
||||
addSignalHandlers() {
|
||||
if (this.handleSignals) {
|
||||
super.addSignalHandlers();
|
||||
}
|
||||
}
|
||||
removeSignalHandlers() { super.removeSignalHandlers(); }
|
||||
exit(code: number) { this.log.push(`exit(${code})`); }
|
||||
getLogger() { return this.logger as MockLogger; }
|
||||
}
|
||||
|
||||
describe('lock()', () => {
|
||||
it('should guard the `fn()` with calls to `create()` and `remove()`', async() => {
|
||||
const fs = getFileSystem();
|
||||
const lockFile = new LockFileUnderTest(fs);
|
||||
|
||||
await lockFile.lock(async() => {
|
||||
lockFile.log.push('fn() - before');
|
||||
// This promise forces node to do a tick in this function, ensuring that we are truly
|
||||
// testing an async scenario.
|
||||
await Promise.resolve();
|
||||
lockFile.log.push('fn() - after');
|
||||
return Promise.resolve();
|
||||
});
|
||||
expect(lockFile.log).toEqual(['create()', 'fn() - before', 'fn() - after', 'remove()']);
|
||||
});
|
||||
|
||||
it('should guard the `fn()` with calls to `create()` and `remove()`, even if it throws',
|
||||
async() => {
|
||||
let error: string = '';
|
||||
const fs = getFileSystem();
|
||||
const lockFile = new LockFileUnderTest(fs);
|
||||
try {
|
||||
await lockFile.lock(async() => {
|
||||
lockFile.log.push('fn()');
|
||||
throw new Error('ERROR');
|
||||
});
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
}
|
||||
expect(error).toEqual('ERROR');
|
||||
expect(lockFile.log).toEqual(['create()', 'fn()', 'remove()']);
|
||||
});
|
||||
|
||||
it('should remove the lockfile if CTRL-C is triggered', async() => {
|
||||
const fs = getFileSystem();
|
||||
const lockFile = new LockFileUnderTest(fs, 100, 3, /* handleSignals */ true);
|
||||
|
||||
await lockFile.lock(async() => {
|
||||
lockFile.log.push('SIGINT');
|
||||
process.emit('SIGINT', 'SIGINT');
|
||||
});
|
||||
// Since the test does not actually exit process, the `remove()` is called one more time.
|
||||
expect(lockFile.log).toEqual(['create()', 'SIGINT', 'remove()', 'exit(1)', 'remove()']);
|
||||
// Clean up the signal handlers. In practice this is not needed since the process would have
|
||||
// been terminated already.
|
||||
lockFile.removeSignalHandlers();
|
||||
});
|
||||
|
||||
it('should remove the lockfile if terminal is closed', async() => {
|
||||
const fs = getFileSystem();
|
||||
const lockFile = new LockFileUnderTest(fs, 100, 3, /* handleSignals */ true);
|
||||
|
||||
await lockFile.lock(async() => {
|
||||
lockFile.log.push('SIGHUP');
|
||||
process.emit('SIGHUP', 'SIGHUP');
|
||||
});
|
||||
// Since this does not actually exit process, the `remove()` is called one more time.
|
||||
expect(lockFile.log).toEqual(['create()', 'SIGHUP', 'remove()', 'exit(1)', 'remove()']);
|
||||
// Clean up the signal handlers. In practice this is not needed since the process would have
|
||||
// been terminated already.
|
||||
lockFile.removeSignalHandlers();
|
||||
});
|
||||
});
|
||||
|
||||
describe('create()', () => {
|
||||
it('should write a lock file to the file-system', async() => {
|
||||
const fs = getFileSystem();
|
||||
const lockFile = new LockFileUnderTest(fs);
|
||||
expect(fs.exists(lockFile.lockFilePath)).toBe(false);
|
||||
await lockFile.create();
|
||||
expect(fs.exists(lockFile.lockFilePath)).toBe(true);
|
||||
});
|
||||
|
||||
it('should retry if another process is locking', async() => {
|
||||
const fs = getFileSystem();
|
||||
const lockFile = new LockFileUnderTest(fs);
|
||||
fs.writeFile(lockFile.lockFilePath, '188');
|
||||
lockFile.remove();
|
||||
expect(fs.exists(lockFile.lockFilePath)).toBe(false);
|
||||
const promise = lockFile.lock(async() => lockFile.log.push('fn()'));
|
||||
// The lock is now waiting on the lockfile becoming free, so no `fn()` in the log.
|
||||
expect(lockFile.log).toEqual(['create()']);
|
||||
expect(lockFile.getLogger().logs.info).toEqual([[
|
||||
'Another process, with id 188, is currently running ngcc.\nWaiting up to 1s for it to finish.'
|
||||
]]);
|
||||
fs.removeFile(lockFile.lockFilePath);
|
||||
// The lockfile has been removed, so we can create our own lockfile, call `fn()` and then
|
||||
// remove the lockfile.
|
||||
await promise;
|
||||
expect(lockFile.log).toEqual(['create()', 'fn()', 'remove()']);
|
||||
});
|
||||
|
||||
it('should not error if the lock file does not exist', () => {
|
||||
const fs = getFileSystem();
|
||||
it('should extend the retry timeout if the other process locking the file changes', async() => {
|
||||
// Use a cached file system to test that we are invalidating it correctly
|
||||
const rawFs = getFileSystem();
|
||||
const fs = new CachedFileSystem(rawFs);
|
||||
const lockFile = new LockFileUnderTest(fs);
|
||||
expect(() => lockFile.remove()).not.toThrow();
|
||||
fs.writeFile(lockFile.lockFilePath, '188');
|
||||
const promise = lockFile.lock(async() => lockFile.log.push('fn()'));
|
||||
// The lock is now waiting on the lockfile becoming free, so no `fn()` in the log.
|
||||
expect(lockFile.log).toEqual(['create()']);
|
||||
expect(lockFile.getLogger().logs.info).toEqual([[
|
||||
'Another process, with id 188, is currently running ngcc.\nWaiting up to 1s for it to finish.'
|
||||
]]);
|
||||
// We need to write to the rawFs to ensure that we don't update the cache at this point
|
||||
rawFs.writeFile(lockFile.lockFilePath, '444');
|
||||
await new Promise(resolve => setTimeout(resolve, 250));
|
||||
expect(lockFile.getLogger().logs.info).toEqual([
|
||||
[
|
||||
'Another process, with id 188, is currently running ngcc.\nWaiting up to 1s for it to finish.'
|
||||
],
|
||||
[
|
||||
'Another process, with id 444, is currently running ngcc.\nWaiting up to 1s for it to finish.'
|
||||
]
|
||||
]);
|
||||
fs.removeFile(lockFile.lockFilePath);
|
||||
// The lockfile has been removed, so we can create our own lockfile, call `fn()` and then
|
||||
// remove the lockfile.
|
||||
await promise;
|
||||
expect(lockFile.log).toEqual(['create()', 'fn()', 'remove()']);
|
||||
});
|
||||
|
||||
it('should error if another process does not release the lockfile before this times out',
|
||||
async() => {
|
||||
const fs = getFileSystem();
|
||||
const lockFile = new LockFileUnderTest(fs, 100, 2);
|
||||
fs.writeFile(lockFile.lockFilePath, '188');
|
||||
const promise = lockFile.lock(async() => lockFile.log.push('fn()'));
|
||||
// The lock is now waiting on the lockfile becoming free, so no `fn()` in the log.
|
||||
expect(lockFile.log).toEqual(['create()']);
|
||||
// Do not remove the lockfile and let the call to `lock()` timeout.
|
||||
let error: Error;
|
||||
await promise.catch(e => error = e);
|
||||
expect(lockFile.log).toEqual(['create()']);
|
||||
expect(error !.message)
|
||||
.toEqual(
|
||||
`Timed out waiting 0.2s for another ngcc process, with id 188, to complete.\n` +
|
||||
`(If you are sure no ngcc process is running then you should delete the lockfile at ${lockFile.lockFilePath}.)`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -8,23 +8,23 @@
|
||||
|
||||
/// <reference types="node" />
|
||||
|
||||
import {SingleProcessExecutor} from '../../src/execution/single_process_executor';
|
||||
import {SingleProcessExecutorSync} from '../../src/execution/single_process_executor';
|
||||
import {SerialTaskQueue} from '../../src/execution/task_selection/serial_task_queue';
|
||||
import {PackageJsonUpdater} from '../../src/writing/package_json_updater';
|
||||
import {MockLockFile} from '../helpers/mock_lock_file';
|
||||
import {MockLockFileSync} from '../helpers/mock_lock_file';
|
||||
import {MockLogger} from '../helpers/mock_logger';
|
||||
|
||||
|
||||
describe('SingleProcessExecutor', () => {
|
||||
let mockLogger: MockLogger;
|
||||
let mockLockFile: MockLockFile;
|
||||
let executor: SingleProcessExecutor;
|
||||
let mockLockFile: MockLockFileSync;
|
||||
let executor: SingleProcessExecutorSync;
|
||||
|
||||
beforeEach(() => {
|
||||
mockLogger = new MockLogger();
|
||||
mockLockFile = new MockLockFile();
|
||||
executor =
|
||||
new SingleProcessExecutor(mockLogger, null as unknown as PackageJsonUpdater, mockLockFile);
|
||||
mockLockFile = new MockLockFileSync();
|
||||
executor = new SingleProcessExecutorSync(
|
||||
mockLogger, null as unknown as PackageJsonUpdater, mockLockFile);
|
||||
});
|
||||
|
||||
describe('execute()', () => {
|
||||
@ -63,11 +63,11 @@ describe('SingleProcessExecutor', () => {
|
||||
});
|
||||
|
||||
it('should not call `analyzeEntryPoints` if Lockfile.create() fails', () => {
|
||||
const lockFile = new MockLockFile({throwOnCreate: true});
|
||||
const lockFile = new MockLockFileSync({throwOnCreate: true});
|
||||
const analyzeFn: () => any = () => { lockFile.log.push('analyzeFn'); };
|
||||
const anyFn: () => any = () => undefined;
|
||||
executor =
|
||||
new SingleProcessExecutor(mockLogger, null as unknown as PackageJsonUpdater, lockFile);
|
||||
executor = new SingleProcessExecutorSync(
|
||||
mockLogger, null as unknown as PackageJsonUpdater, lockFile);
|
||||
let error = '';
|
||||
try {
|
||||
executor.execute(analyzeFn, anyFn);
|
||||
@ -81,9 +81,9 @@ describe('SingleProcessExecutor', () => {
|
||||
it('should fail if Lockfile.remove() fails', () => {
|
||||
const noTasks = () => new SerialTaskQueue([] as any);
|
||||
const anyFn: () => any = () => undefined;
|
||||
const lockFile = new MockLockFile({throwOnRemove: true});
|
||||
executor =
|
||||
new SingleProcessExecutor(mockLogger, null as unknown as PackageJsonUpdater, lockFile);
|
||||
const lockFile = new MockLockFileSync({throwOnRemove: true});
|
||||
executor = new SingleProcessExecutorSync(
|
||||
mockLogger, null as unknown as PackageJsonUpdater, lockFile);
|
||||
let error = '';
|
||||
try {
|
||||
executor.execute(noTasks, anyFn);
|
||||
|
Reference in New Issue
Block a user