diff --git a/extension/src/tasks/taskUtil.ts b/extension/src/tasks/taskUtil.ts index 900d6c5f1..c57647729 100644 --- a/extension/src/tasks/taskUtil.ts +++ b/extension/src/tasks/taskUtil.ts @@ -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'; @@ -246,35 +246,26 @@ function getVSCodeTasksFromGradleProject( gradleProject: GradleProject, client: GradleClient ): vscode.Task[] { - const gradleTasks: GradleTask[] | void = gradleProject.getTasksList(); - const vsCodeTasks = []; - try { - vsCodeTasks.push( - ...gradleTasks.map((gradleTask) => + const projects: Array = [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 - ); + ); + } + for (const childProject of project!.getProjectsList()) { + projects.push(childProject); + } } - gradleProject.getProjectsList().forEach((project) => { - vsCodeTasks.push( - ...getVSCodeTasksFromGradleProject( - taskTerminalsStore, - rootProject, - project, - client - ) - ); - }); + return vsCodeTasks; } @@ -290,14 +281,14 @@ export async function loadTasksForProjectRoots( client: GradleClient, rootProjects: ReadonlyArray ): Promise { - 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( + allTasks = allTasks.concat( + getVSCodeTasksFromGradleProject( taskTerminalsStore, rootProject, gradleProject, diff --git a/extension/src/test/unit/taskUtil.test.ts b/extension/src/test/unit/taskUtil.test.ts new file mode 100644 index 000000000..903f7ad69 --- /dev/null +++ b/extension/src/test/unit/taskUtil.test.ts @@ -0,0 +1,150 @@ +/* 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'; + +function buildProject( + project: string, + rootProject: string, + isRoot: boolean, + tasksPerProject: number +): GradleProject { + const gradleProject = new GradleProject(); + gradleProject.setIsRoot(isRoot); + + const tasks: Array = []; + 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 { + const projects: Array = []; + 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.only(getSuiteName('taskUtil'), () => { + const workspaceFolder: vscode.WorkspaceFolder = { + index: 0, + uri: vscode.Uri.file('folder1'), + name: 'folder1', + }; + let client: any; + let taskTerminalsStore: TaskTerminalsStore; + + beforeEach(() => { + client = buildMockClient(); + taskTerminalsStore = new TaskTerminalsStore(); + }); + afterEach(() => { + sinon.restore(); + }); + + it('should create vscode tasks', async () => { + const rootProject = new RootProject( + workspaceFolder, + vscode.Uri.file('folder1'), + { + tasks: [], + clean: false, + } + ); + 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); + client.getBuild.resolves(Promise.resolve(gradleBuild)); + + const tasks = await loadTasksForProjectRoots(taskTerminalsStore, client, [ + rootProject, + ]); + assert.strictEqual(tasks.length, 2); + }); + + it.only('should create vscode tasks for a super-massive project without throwing a callstack error', async () => { + const rootProject = new RootProject( + workspaceFolder, + vscode.Uri.file('folder1'), + { + tasks: [], + clean: false, + } + ); + const gradleBuild = new GradleBuild(); + const rootGradleProject = buildProject( + 'root-project', + 'root-project', + true, + 1 + ); + + const generateProjects = 50; + const generateLevels = 3; + const generateTasks = 3; + + const projectsList = getDeeplyNestedProjectTree( + generateProjects, + generateTasks, + generateLevels + ); + + rootGradleProject.setProjectsList(projectsList); + gradleBuild.setProject(rootGradleProject); + client.getBuild.resolves(Promise.resolve(gradleBuild)); + + const tasks = await loadTasksForProjectRoots(taskTerminalsStore, client, [ + rootProject, + ]); + assert.strictEqual(tasks.length, 382651); + }); +});