Merge pull request #119084 from microsoft/tyriar/118256_test

Task dependsOn test and refactors
This commit is contained in:
Daniel Imms 2021-03-16 09:41:02 -07:00 committed by GitHub
commit 3e05837735
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 278 additions and 222 deletions

View file

@ -1,2 +1,3 @@
out
node_modules
node_modules
testWorkspace/.vscode/tasks.json

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { window, tasks, Disposable, TaskDefinition, Task, EventEmitter, CustomExecution, Pseudoterminal, TaskScope, commands, env, UIKind, ShellExecution, TaskExecution, Terminal, Event, workspace, ConfigurationTarget } from 'vscode';
import { window, tasks, Disposable, TaskDefinition, Task, EventEmitter, CustomExecution, Pseudoterminal, TaskScope, commands, env, UIKind, ShellExecution, TaskExecution, Terminal, Event, workspace, ConfigurationTarget, TaskProcessStartEvent } from 'vscode';
import { assertNoRpc } from '../utils';
// Disable tasks tests:
@ -32,245 +32,300 @@ import { assertNoRpc } from '../utils';
disposables.length = 0;
});
test('CustomExecution task should start and shutdown successfully', async () => {
interface CustomTestingTaskDefinition extends TaskDefinition {
/**
* One of the task properties. This can be used to customize the task in the tasks.json
*/
customProp1: string;
}
const taskType: string = 'customTesting';
const taskName = 'First custom task';
let isPseudoterminalClosed = false;
// There's a strict order that should be observed here:
// 1. The terminal opens
// 2. The terminal is written to.
// 3. The terminal is closed.
enum TestOrder {
Start,
TerminalOpened,
TerminalWritten,
TerminalClosed
}
let testOrder = TestOrder.Start;
// Launch the task
const terminal = await new Promise<Terminal>(r => {
disposables.push(window.onDidOpenTerminal(e => {
assert.strictEqual(testOrder, TestOrder.Start);
testOrder = TestOrder.TerminalOpened;
r(e);
}));
disposables.push(tasks.registerTaskProvider(taskType, {
provideTasks: () => {
const result: Task[] = [];
const kind: CustomTestingTaskDefinition = {
type: taskType,
customProp1: 'testing task one'
};
const writeEmitter = new EventEmitter<string>();
const execution = new CustomExecution((): Thenable<Pseudoterminal> => {
const pty: Pseudoterminal = {
onDidWrite: writeEmitter.event,
open: () => writeEmitter.fire('testing\r\n'),
close: () => isPseudoterminalClosed = true
};
return Promise.resolve(pty);
suite('ShellExecution', () => {
test('Execution from onDidEndTaskProcess and onDidStartTaskProcess are equal to original', () => {
return new Promise<void>(async (resolve) => {
const task = new Task({ type: 'testTask' }, TaskScope.Workspace, 'echo', 'testTask', new ShellExecution('echo', ['hello test']));
let taskExecution: TaskExecution | undefined;
const executeDoneEvent: EventEmitter<void> = new EventEmitter();
const taskExecutionShouldBeSet: Promise<void> = new Promise(resolve => {
const disposable = executeDoneEvent.event(() => {
resolve();
disposable.dispose();
});
const task = new Task(kind, TaskScope.Workspace, taskName, taskType, execution);
result.push(task);
return result;
});
let count = 2;
const progressMade: EventEmitter<void> = new EventEmitter();
let startSucceeded = false;
let endSucceeded = false;
disposables.push(progressMade.event(() => {
count--;
if ((count === 0) && startSucceeded && endSucceeded) {
resolve();
}
}));
disposables.push(tasks.onDidStartTaskProcess(async (e) => {
await taskExecutionShouldBeSet;
if (e.execution === taskExecution) {
startSucceeded = true;
progressMade.fire();
}
}));
disposables.push(tasks.onDidEndTaskProcess(async (e) => {
await taskExecutionShouldBeSet;
if (e.execution === taskExecution) {
endSucceeded = true;
progressMade.fire();
}
}));
taskExecution = await tasks.executeTask(task);
executeDoneEvent.fire();
});
});
test('dependsOn task should start with a different processId (#118256)', async () => {
// Set up dependsOn task by creating tasks.json since this is not possible via the API
// Tasks API
const tasksConfig = workspace.getConfiguration('tasks');
await tasksConfig.update('version', '2.0.0', ConfigurationTarget.Workspace);
await tasksConfig.update('tasks', [
{
label: 'taskToDependOn',
type: 'shell',
command: 'sleep 1',
problemMatcher: []
},
resolveTask(_task: Task): Task | undefined {
assert.fail('resolveTask should not trigger during the test');
{
label: 'Run this task',
type: 'shell',
command: 'sleep 1',
problemMatcher: [],
dependsOn: 'taskToDependOn'
}
}));
commands.executeCommand('workbench.action.tasks.runTask', `${taskType}: ${taskName}`);
});
], ConfigurationTarget.Workspace);
// Verify the output
await new Promise<void>(r => {
disposables.push(window.onDidWriteTerminalData(e => {
if (e.terminal !== terminal) {
return;
}
assert.strictEqual(testOrder, TestOrder.TerminalOpened);
testOrder = TestOrder.TerminalWritten;
assert.notStrictEqual(terminal, undefined);
assert.strictEqual(e.data, 'testing\r\n');
r();
}));
});
// Run the task
commands.executeCommand('workbench.action.tasks.runTask', 'Run this task');
// Dispose the terminal
await new Promise<void>(r => {
disposables.push(window.onDidCloseTerminal(() => {
assert.strictEqual(testOrder, TestOrder.TerminalWritten);
testOrder = TestOrder.TerminalClosed;
// Pseudoterminal.close should have fired by now, additionally we want
// to make sure all events are flushed before continuing with more tests
assert.ok(isPseudoterminalClosed);
r();
}));
terminal.dispose();
});
});
test('sync CustomExecution task should flush all data on close', async () => {
interface CustomTestingTaskDefinition extends TaskDefinition {
/**
* One of the task properties. This can be used to customize the task in the tasks.json
*/
customProp1: string;
}
const taskType: string = 'customTesting';
const taskName = 'First custom task';
// Launch the task
const terminal = await new Promise<Terminal>(r => {
disposables.push(window.onDidOpenTerminal(e => r(e)));
disposables.push(tasks.registerTaskProvider(taskType, {
provideTasks: () => {
const result: Task[] = [];
const kind: CustomTestingTaskDefinition = {
type: taskType,
customProp1: 'testing task one'
};
const writeEmitter = new EventEmitter<string>();
const closeEmitter = new EventEmitter<void>();
const execution = new CustomExecution((): Thenable<Pseudoterminal> => {
const pty: Pseudoterminal = {
onDidWrite: writeEmitter.event,
onDidClose: closeEmitter.event,
open: () => {
writeEmitter.fire('exiting');
closeEmitter.fire();
},
close: () => { }
};
return Promise.resolve(pty);
});
const task = new Task(kind, TaskScope.Workspace, taskName, taskType, execution);
result.push(task);
return result;
},
resolveTask(_task: Task): Task | undefined {
assert.fail('resolveTask should not trigger during the test');
}
}));
commands.executeCommand('workbench.action.tasks.runTask', `${taskType}: ${taskName}`);
});
// Verify the output
await new Promise<void>(r => {
disposables.push(window.onDidWriteTerminalData(e => {
if (e.terminal !== terminal) {
return;
}
assert.strictEqual(e.data, 'exiting');
r();
}));
});
// Dispose the terminal
await new Promise<void>(r => {
disposables.push(window.onDidCloseTerminal(() => r()));
terminal.dispose();
});
});
test('Execution from onDidEndTaskProcess and onDidStartTaskProcess are equal to original', () => {
return new Promise<void>(async (resolve) => {
const task = new Task({ type: 'testTask' }, TaskScope.Workspace, 'echo', 'testTask', new ShellExecution('echo', ['hello test']));
let taskExecution: TaskExecution | undefined;
const executeDoneEvent: EventEmitter<void> = new EventEmitter();
const taskExecutionShouldBeSet: Promise<void> = new Promise(resolve => {
const disposable = executeDoneEvent.event(() => {
resolve();
disposable.dispose();
// Listen for first task and verify valid process ID
const startEvent1 = await new Promise<TaskProcessStartEvent>(r => {
const listener = tasks.onDidStartTaskProcess(async (e) => {
if (e.execution.task.name === 'taskToDependOn') {
listener.dispose();
r(e);
}
});
});
let count = 2;
const progressMade: EventEmitter<void> = new EventEmitter();
let startSucceeded = false;
let endSucceeded = false;
disposables.push(progressMade.event(() => {
count--;
if ((count === 0) && startSucceeded && endSucceeded) {
resolve();
}
}));
assert.ok(startEvent1.processId);
// Listen for second task, verify valid process ID and that it's not the process ID of
// the first task
const startEvent2 = await new Promise<TaskProcessStartEvent>(r => {
const listener = tasks.onDidStartTaskProcess(async (e) => {
if (e.execution.task.name === 'Run this task') {
listener.dispose();
r(e);
}
});
});
assert.ok(startEvent2.processId);
assert.notStrictEqual(startEvent1.processId, startEvent2.processId);
disposables.push(tasks.onDidStartTaskProcess(async (e) => {
await taskExecutionShouldBeSet;
if (e.execution === taskExecution) {
startSucceeded = true;
progressMade.fire();
}
}));
disposables.push(tasks.onDidEndTaskProcess(async (e) => {
await taskExecutionShouldBeSet;
if (e.execution === taskExecution) {
endSucceeded = true;
progressMade.fire();
}
}));
taskExecution = await tasks.executeTask(task);
executeDoneEvent.fire();
// Clear out tasks config
await tasksConfig.update('tasks', []);
});
});
// https://github.com/microsoft/vscode/issues/100577
test('A CustomExecution task can be fetched and executed', () => {
return new Promise<void>(async (resolve, reject) => {
class CustomTerminal implements Pseudoterminal {
private readonly writeEmitter = new EventEmitter<string>();
public readonly onDidWrite: Event<string> = this.writeEmitter.event;
public async close(): Promise<void> { }
private closeEmitter = new EventEmitter<void>();
onDidClose: Event<void> = this.closeEmitter.event;
public open(): void {
this.closeEmitter.fire();
resolve();
}
suite('CustomExecution', () => {
test('task should start and shutdown successfully', async () => {
interface CustomTestingTaskDefinition extends TaskDefinition {
/**
* One of the task properties. This can be used to customize the task in the tasks.json
*/
customProp1: string;
}
const taskType: string = 'customTesting';
const taskName = 'First custom task';
let isPseudoterminalClosed = false;
// There's a strict order that should be observed here:
// 1. The terminal opens
// 2. The terminal is written to.
// 3. The terminal is closed.
enum TestOrder {
Start,
TerminalOpened,
TerminalWritten,
TerminalClosed
}
function buildTask(): Task {
const task = new Task(
{
type: 'customTesting',
let testOrder = TestOrder.Start;
// Launch the task
const terminal = await new Promise<Terminal>(r => {
disposables.push(window.onDidOpenTerminal(e => {
assert.strictEqual(testOrder, TestOrder.Start);
testOrder = TestOrder.TerminalOpened;
r(e);
}));
disposables.push(tasks.registerTaskProvider(taskType, {
provideTasks: () => {
const result: Task[] = [];
const kind: CustomTestingTaskDefinition = {
type: taskType,
customProp1: 'testing task one'
};
const writeEmitter = new EventEmitter<string>();
const execution = new CustomExecution((): Thenable<Pseudoterminal> => {
const pty: Pseudoterminal = {
onDidWrite: writeEmitter.event,
open: () => writeEmitter.fire('testing\r\n'),
close: () => isPseudoterminalClosed = true
};
return Promise.resolve(pty);
});
const task = new Task(kind, TaskScope.Workspace, taskName, taskType, execution);
result.push(task);
return result;
},
TaskScope.Workspace,
'Test Task',
'customTesting',
new CustomExecution(
async (): Promise<Pseudoterminal> => {
return new CustomTerminal();
}
)
);
return task;
}
resolveTask(_task: Task): Task | undefined {
assert.fail('resolveTask should not trigger during the test');
}
}));
commands.executeCommand('workbench.action.tasks.runTask', `${taskType}: ${taskName}`);
});
disposables.push(tasks.registerTaskProvider('customTesting', {
provideTasks: () => {
return [buildTask()];
},
resolveTask(_task: Task): undefined {
return undefined;
// Verify the output
await new Promise<void>(r => {
disposables.push(window.onDidWriteTerminalData(e => {
if (e.terminal !== terminal) {
return;
}
assert.strictEqual(testOrder, TestOrder.TerminalOpened);
testOrder = TestOrder.TerminalWritten;
assert.notStrictEqual(terminal, undefined);
assert.strictEqual(e.data, 'testing\r\n');
r();
}));
});
// Dispose the terminal
await new Promise<void>(r => {
disposables.push(window.onDidCloseTerminal(() => {
assert.strictEqual(testOrder, TestOrder.TerminalWritten);
testOrder = TestOrder.TerminalClosed;
// Pseudoterminal.close should have fired by now, additionally we want
// to make sure all events are flushed before continuing with more tests
assert.ok(isPseudoterminalClosed);
r();
}));
terminal.dispose();
});
});
test('sync task should flush all data on close', async () => {
interface CustomTestingTaskDefinition extends TaskDefinition {
/**
* One of the task properties. This can be used to customize the task in the tasks.json
*/
customProp1: string;
}
const taskType: string = 'customTesting';
const taskName = 'First custom task';
// Launch the task
const terminal = await new Promise<Terminal>(r => {
disposables.push(window.onDidOpenTerminal(e => r(e)));
disposables.push(tasks.registerTaskProvider(taskType, {
provideTasks: () => {
const result: Task[] = [];
const kind: CustomTestingTaskDefinition = {
type: taskType,
customProp1: 'testing task one'
};
const writeEmitter = new EventEmitter<string>();
const closeEmitter = new EventEmitter<void>();
const execution = new CustomExecution((): Thenable<Pseudoterminal> => {
const pty: Pseudoterminal = {
onDidWrite: writeEmitter.event,
onDidClose: closeEmitter.event,
open: () => {
writeEmitter.fire('exiting');
closeEmitter.fire();
},
close: () => { }
};
return Promise.resolve(pty);
});
const task = new Task(kind, TaskScope.Workspace, taskName, taskType, execution);
result.push(task);
return result;
},
resolveTask(_task: Task): Task | undefined {
assert.fail('resolveTask should not trigger during the test');
}
}));
commands.executeCommand('workbench.action.tasks.runTask', `${taskType}: ${taskName}`);
});
// Verify the output
await new Promise<void>(r => {
disposables.push(window.onDidWriteTerminalData(e => {
if (e.terminal !== terminal) {
return;
}
assert.strictEqual(e.data, 'exiting');
r();
}));
});
// Dispose the terminal
await new Promise<void>(r => {
disposables.push(window.onDidCloseTerminal(() => r()));
terminal.dispose();
});
});
test('A task can be fetched and executed (#100577)', () => {
return new Promise<void>(async (resolve, reject) => {
class CustomTerminal implements Pseudoterminal {
private readonly writeEmitter = new EventEmitter<string>();
public readonly onDidWrite: Event<string> = this.writeEmitter.event;
public async close(): Promise<void> { }
private closeEmitter = new EventEmitter<void>();
onDidClose: Event<void> = this.closeEmitter.event;
public open(): void {
this.closeEmitter.fire();
resolve();
}
}
}));
const task = await tasks.fetchTasks({ type: 'customTesting' });
function buildTask(): Task {
const task = new Task(
{
type: 'customTesting',
},
TaskScope.Workspace,
'Test Task',
'customTesting',
new CustomExecution(
async (): Promise<Pseudoterminal> => {
return new CustomTerminal();
}
)
);
return task;
}
if (task && task.length > 0) {
await tasks.executeTask(task[0]);
} else {
reject('fetched task can\'t be undefined');
}
disposables.push(tasks.registerTaskProvider('customTesting', {
provideTasks: () => {
return [buildTask()];
},
resolveTask(_task: Task): undefined {
return undefined;
}
}));
const task = await tasks.fetchTasks({ type: 'customTesting' });
if (task && task.length > 0) {
await tasks.executeTask(task[0]);
} else {
reject('fetched task can\'t be undefined');
}
});
});
});
});