Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve code performance and fix maximum callstack error #734

Merged
merged 2 commits into from
Oct 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 19 additions & 31 deletions extension/src/tasks/taskUtil.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as vscode from 'vscode';
import { parseArgsStringToArgv } from 'string-argv';
import { GradleTask, GradleProject, GradleBuild } from '../proto/gradle_pb';
import { GradleProject, GradleBuild, GradleTask } from '../proto/gradle_pb';
import { TaskArgs } from '../stores/types';
import { GradleTaskDefinition } from '.';
import { GradleRunnerTerminal } from '../terminal';
Expand Down Expand Up @@ -246,35 +246,24 @@ function getVSCodeTasksFromGradleProject(
gradleProject: GradleProject,
client: GradleClient
): vscode.Task[] {
const gradleTasks: GradleTask[] | void = gradleProject.getTasksList();
const vsCodeTasks = [];
try {
vsCodeTasks.push(
...gradleTasks.map((gradleTask) =>
let projects: Array<GradleProject> = [gradleProject];
const vsCodeTasks: vscode.Task[] = [];
while (projects.length) {
const project = projects.pop();
const gradleTasks: GradleTask[] | void = project!.getTasksList();
for (const gradleTask of gradleTasks) {
vsCodeTasks.push(
createVSCodeTaskFromGradleTask(
taskTerminalsStore,
gradleTask,
rootProject,
client
)
)
);
} catch (err) {
logger.error(
'Unable to generate vscode tasks from gradle tasks:',
err.message
);
);
}
projects = projects.concat(project!.getProjectsList());
}
gradleProject.getProjectsList().forEach((project) => {
vsCodeTasks.push(
...getVSCodeTasksFromGradleProject(
taskTerminalsStore,
rootProject,
project,
client
)
);
});

return vsCodeTasks;
}

Expand All @@ -290,20 +279,19 @@ export async function loadTasksForProjectRoots(
client: GradleClient,
rootProjects: ReadonlyArray<RootProject>
): Promise<vscode.Task[]> {
const allTasks: vscode.Task[] = [];
let allTasks: vscode.Task[] = [];
for (const rootProject of rootProjects) {
if (getConfigIsAutoDetectionEnabled(rootProject)) {
const gradleBuild = await getGradleBuild(client, rootProject);
const gradleProject = gradleBuild && gradleBuild.getProject();
if (gradleProject) {
allTasks.push(
...getVSCodeTasksFromGradleProject(
taskTerminalsStore,
rootProject,
gradleProject,
client
)
const vsCodeTasks = getVSCodeTasksFromGradleProject(
taskTerminalsStore,
rootProject,
gradleProject,
client
);
allTasks = allTasks.concat(vsCodeTasks);
}
}
}
Expand Down
10 changes: 6 additions & 4 deletions extension/src/terminal/GradleRunnerTerminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const nlRegExp = new RegExp(`${NL}([^${CR}]|$)`, 'g');

export class GradleRunnerTerminal {
private readonly writeEmitter = new vscode.EventEmitter<string>();
private stdOutLoggerStream: LoggerStream;
private stdOutLoggerStream: LoggerStream | undefined;
private readonly closeEmitter = new vscode.EventEmitter<void>();
private task?: vscode.Task;
public readonly onDidWrite: vscode.Event<string> = this.writeEmitter.event;
Expand All @@ -29,8 +29,10 @@ export class GradleRunnerTerminal {
private readonly cancellationKey: string,
private readonly client: GradleClient
) {
// TODO: this is only needed for the tests. Find a better way to test task output in the tests.
this.stdOutLoggerStream = new LoggerStream(logger, LogVerbosity.INFO);
if (isTest()) {
// TODO: this is only needed for the tests. Find a better way to test task output in the tests.
this.stdOutLoggerStream = new LoggerStream(logger, LogVerbosity.INFO);
}
}

public async open(): Promise<void> {
Expand Down Expand Up @@ -121,7 +123,7 @@ export class GradleRunnerTerminal {
private handleOutput = (output: Output): void => {
const messageBytes = output.getOutputBytes_asU8();
if (messageBytes.length) {
if (isTest()) {
if (isTest() && this.stdOutLoggerStream) {
this.stdOutLoggerStream.write(messageBytes);
}
this.write(new util.TextDecoder('utf-8').decode(messageBytes));
Expand Down
134 changes: 134 additions & 0 deletions extension/src/test/unit/taskUtil.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/* eslint-disable sonarjs/no-duplicate-string */
/* eslint-disable @typescript-eslint/no-explicit-any */

import * as assert from 'assert';
import * as sinon from 'sinon';
import * as vscode from 'vscode';
import { GradleBuild, GradleProject, GradleTask } from '../../proto/gradle_pb';
import { RootProject } from '../../rootProject/RootProject';
import { TaskTerminalsStore } from '../../stores';
import { loadTasksForProjectRoots } from '../../tasks/taskUtil';
import { buildMockClient, getSuiteName } from '../testUtil';

const mockWorkspaceFolder: vscode.WorkspaceFolder = {
index: 0,
uri: vscode.Uri.file('folder1'),
name: 'folder1',
};

const mockRootProject = new RootProject(
mockWorkspaceFolder,
vscode.Uri.file('folder1'),
{
tasks: [],
clean: false,
}
);
const mockClient = buildMockClient();
const mockTaskTerminalsStore = new TaskTerminalsStore();

function buildProject(
project: string,
rootProject: string,
isRoot: boolean,
tasksPerProject: number
): GradleProject {
const gradleProject = new GradleProject();
gradleProject.setIsRoot(isRoot);

const tasks: Array<GradleTask> = [];
for (let i = 0; i < tasksPerProject; i++) {
const gradleTask = new GradleTask();
gradleTask.setName(`test-${project}-task-name-${Math.random()}`);
gradleTask.setProject(project);
gradleTask.setRootproject(rootProject);
tasks.push(gradleTask);
}

gradleProject.setTasksList(tasks);
return gradleProject;
}

function getDeeplyNestedProjectTree(
amountOfProjects: number,
tasksPerProject: number,
levels: number,
currentLevel = 1
): Array<GradleProject> {
const projects: Array<GradleProject> = [];
for (let i = 0; i < amountOfProjects; i++) {
const project = buildProject(
'child-project',
'root-project',
false,
tasksPerProject
);
if (currentLevel < levels) {
project.setProjectsList(
getDeeplyNestedProjectTree(
amountOfProjects,
tasksPerProject,
levels,
currentLevel + 1
)
);
}
projects.push(project);
}
return projects;
}

describe(getSuiteName('taskUtil'), () => {
afterEach(() => {
sinon.restore();
});

it('should create vscode tasks', async () => {
const gradleBuild = new GradleBuild();
const rootGradleProject = buildProject(
'root-project',
'root-project',
true,
1
);
const childGradleProject = buildProject(
'child-project',
'root-project',
false,
1
);
rootGradleProject.setProjectsList([childGradleProject]);
gradleBuild.setProject(rootGradleProject);
mockClient.getBuild.resolves(Promise.resolve(gradleBuild));

const tasks = await loadTasksForProjectRoots(
mockTaskTerminalsStore,
mockClient,
[mockRootProject]
);
assert.strictEqual(tasks.length, 2);
});

it('should create vscode tasks for a super-massive project without throwing a callstack error', async () => {
const gradleBuild = new GradleBuild();
const rootGradleProject = buildProject(
'root-project',
'root-project',
true,
1
);

const projectsList = getDeeplyNestedProjectTree(50, 2, 3);

rootGradleProject.setProjectsList(projectsList);
gradleBuild.setProject(rootGradleProject);
mockClient.getBuild.resolves(Promise.resolve(gradleBuild));

const tasks = await loadTasksForProjectRoots(
mockTaskTerminalsStore,
mockClient,
[mockRootProject]
);
assert.strictEqual(tasks.length, 255101);
});
});