Skip to content

Commit

Permalink
Improve code performance and fix maximum callstack error (#734)
Browse files Browse the repository at this point in the history
* Improve code performance and fix maximum callstack error

* Further perf improvements & code org
  • Loading branch information
badsyntax authored Oct 5, 2020
1 parent a04a05b commit 4191a3b
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 35 deletions.
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);
});
});

0 comments on commit 4191a3b

Please sign in to comment.