Skip to content

Commit

Permalink
Setup workspace project tests to run in parallel (#788)
Browse files Browse the repository at this point in the history
  • Loading branch information
MicroFish91 authored Dec 17, 2024
1 parent 05bee27 commit 874ff43
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 88 deletions.
97 changes: 97 additions & 0 deletions test/nightly/deployWorkspaceProject/buildParallelScenarios.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { runWithTestActionContext } from "@microsoft/vscode-azext-dev";
import * as assert from "assert";
import * as path from "path";
import { workspace, type Uri, type WorkspaceFolder } from "vscode";
import { AzExtFsExtra, deployWorkspaceProject, dwpSettingUtilsV2, ext, parseError, settingUtils, type DeploymentConfigurationSettings, type DeployWorkspaceProjectResults, type IParsedError } from "../../../extension.bundle";
import { assertStringPropsMatch, getWorkspaceFolderUri } from "../../testUtils";
import { resourceGroupsToDelete } from "../global.nightly.test";
import { dwpTestScenarios, type DeployWorkspaceProjectTestScenario } from "./dwpTestScenarios";

export interface DwpParallelTestScenario {
title: string;
callback(setupTask: Promise<void>): Promise<void>;
scenario?: Promise<void>;
}

export function buildParallelTestScenarios(): DwpParallelTestScenario[] {
return dwpTestScenarios.map(scenario => {
return {
title: scenario.label,
callback: buildParallelScenarioCallback(scenario),
};
});
}

function buildParallelScenarioCallback(scenario: DeployWorkspaceProjectTestScenario): DwpParallelTestScenario['callback'] {
return async (setupTask: Promise<void>) => {
await setupTask;

const workspaceFolderUri: Uri = getWorkspaceFolderUri(scenario.folderName);
const rootFolder: WorkspaceFolder | undefined = workspace.getWorkspaceFolder(workspaceFolderUri);
assert.ok(rootFolder, 'Could not retrieve root workspace folder.');

await cleanWorkspaceFolderSettings(rootFolder);

for (const testCase of scenario.testCases) {
ext.outputChannel.appendLog(`[[[ *** ${scenario.label} - ${testCase.label} *** ]]]`);
await runWithTestActionContext('deployWorkspaceProject', async context => {
await context.ui.runWithInputs(testCase.inputs, async () => {
let results: DeployWorkspaceProjectResults;
let perr: IParsedError | undefined;
try {
results = await deployWorkspaceProject(context);
} catch (e) {
results = {};

perr = parseError(e);
console.log(perr);
}

if (testCase.resourceGroupToDelete) {
resourceGroupsToDelete.add(testCase.resourceGroupToDelete);
}

// Verify 'expectedErrMsg'
if (perr || testCase.expectedErrMsg) {
if (testCase.expectedErrMsg instanceof RegExp) {
assert.match(perr?.message ?? "", testCase.expectedErrMsg, 'DeployWorkspaceProject thrown and expected error message did not match.');
} else {
assert.strictEqual(perr?.message ?? "", testCase.expectedErrMsg, 'DeployWorkspaceProject thrown and expected error message did not match.');
}
}

// Verify 'expectedResults'
assertStringPropsMatch(results as Partial<Record<string, string>>, (testCase.expectedResults ?? {}) as Record<string, string | RegExp>, 'DeployWorkspaceProject results mismatch.');

// Verify 'expectedVSCodeSettings'
const deploymentConfigurationsV2: DeploymentConfigurationSettings[] = await dwpSettingUtilsV2.getWorkspaceDeploymentConfigurations(rootFolder) ?? [];
const expectedDeploymentConfigurations = testCase.expectedVSCodeSettings?.deploymentConfigurations ?? [];
assert.strictEqual(deploymentConfigurationsV2.length, expectedDeploymentConfigurations.length, 'DeployWorkspaceProject ".vscode" saved settings mismatch.');

for (const [i, expectedDeploymentConfiguration] of expectedDeploymentConfigurations.entries()) {
const deploymentConfiguration: DeploymentConfigurationSettings = deploymentConfigurationsV2[i] ?? {};
assertStringPropsMatch(deploymentConfiguration as Partial<Record<string, string>>, expectedDeploymentConfiguration, 'DeployWorkspaceProject ".vscode" saved settings mismatch.');
}

// Verify 'postTestAssertion'
await testCase.postTestAssertion?.(context, results, 'DeployWorkspaceProject resource settings mismatch.');
});
});
}

await cleanWorkspaceFolderSettings(rootFolder);
}
}

async function cleanWorkspaceFolderSettings(rootFolder: WorkspaceFolder) {
const settingsPath: string = settingUtils.getDefaultRootWorkspaceSettingsPath(rootFolder);
const vscodeFolderPath: string = path.dirname(settingsPath);
if (await AzExtFsExtra.pathExists(vscodeFolderPath)) {
await AzExtFsExtra.deleteResource(vscodeFolderPath, { recursive: true });
}
}
118 changes: 31 additions & 87 deletions test/nightly/deployWorkspaceProject/deployWorkspaceProject.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
* Licensed under the MIT License. See LICENSE.md in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { type ManagedEnvironment } from '@azure/arm-appcontainers';
import { runWithTestActionContext } from '@microsoft/vscode-azext-dev';
import { nonNullProp, parseError, randomUtils, type IParsedError } from "@microsoft/vscode-azext-utils";
import * as assert from 'assert';
import * as path from 'path';
import { workspace, type Uri, type WorkspaceFolder } from 'vscode';
import { AzExtFsExtra, createManagedEnvironment, deployWorkspaceProject, dwpSettingUtilsV2, settingUtils, type DeploymentConfigurationSettings, type DeployWorkspaceProjectResults } from '../../../extension.bundle';
import { type ManagedEnvironment } from "@azure/arm-appcontainers";
import { runWithTestActionContext } from "@microsoft/vscode-azext-dev";
import { nonNullProp, randomUtils } from "@microsoft/vscode-azext-utils";
import * as assert from "assert";
import { createManagedEnvironment } from "../../../extension.bundle";
import { longRunningTestsEnabled } from '../../global.test';
import { assertStringPropsMatch, getWorkspaceFolderUri } from '../../testUtils';
import { resourceGroupsToDelete } from '../global.nightly.test';
import { dwpTestScenarios } from './dwpTestScenarios';
import { resourceGroupsToDelete } from "../global.nightly.test";
import { buildParallelTestScenarios, type DwpParallelTestScenario } from './buildParallelScenarios';

suite('deployWorkspaceProject', function (this: Mocha.Suite) {
let setupTask: Promise<void>;
const testScenarios: DwpParallelTestScenario[] = buildParallelTestScenarios();

suite('deployWorkspaceProject', async function (this: Mocha.Suite) {
this.timeout(15 * 60 * 1000);

suiteSetup(async function (this: Mocha.Context) {
Expand All @@ -26,89 +26,33 @@ suite('deployWorkspaceProject', function (this: Mocha.Suite) {
// Create a managed environment first so that we can guarantee one is always built before workspace deployment tests start.
// This is crucial for test consistency because the managed environment prompt will skip if no managed environment
// resources are available yet. Creating at least one environment first ensures consistent reproduceability.
const managedEnvironment: ManagedEnvironment | undefined = await setupManagedEnvironment();
assert.ok(managedEnvironment, 'Failed to create managed environment - skipping "deployWorkspaceProject" tests.');
resourceGroupsToDelete.add(nonNullProp(managedEnvironment, 'name'));
});

for (const scenario of dwpTestScenarios) {
suite(scenario.label, function () {
const workspaceFolderUri: Uri = getWorkspaceFolderUri(scenario.folderName);
const rootFolder: WorkspaceFolder | undefined = workspace.getWorkspaceFolder(workspaceFolderUri);
assert.ok(rootFolder, 'Could not retrieve root workspace folder.');

suiteSetup(getMethodCleanWorkspaceFolderSettings(rootFolder));
suiteTeardown(getMethodCleanWorkspaceFolderSettings(rootFolder));

for (const testCase of scenario.testCases) {
test(testCase.label, async function () {
await runWithTestActionContext('deployWorkspaceProject', async context => {
await context.ui.runWithInputs(testCase.inputs, async () => {
let results: DeployWorkspaceProjectResults;
let perr: IParsedError | undefined;
try {
results = await deployWorkspaceProject(context);
} catch (e) {
results = {};

perr = parseError(e);
console.log(perr);
}

if (testCase.resourceGroupToDelete) {
resourceGroupsToDelete.add(testCase.resourceGroupToDelete);
}

// Verify 'expectedErrMsg'
if (perr || testCase.expectedErrMsg) {
if (testCase.expectedErrMsg instanceof RegExp) {
assert.match(perr?.message ?? "", testCase.expectedErrMsg, 'DeployWorkspaceProject thrown and expected error message did not match.');
} else {
assert.strictEqual(perr?.message ?? "", testCase.expectedErrMsg, 'DeployWorkspaceProject thrown and expected error message did not match.');
}
}

// Verify 'expectedResults'
assertStringPropsMatch(results as Partial<Record<string, string>>, (testCase.expectedResults ?? {}) as Record<string, string | RegExp>, 'DeployWorkspaceProject results mismatch.');
setupTask = setupManagedEnvironment();

// Verify 'expectedVSCodeSettings'
const deploymentConfigurationsV2: DeploymentConfigurationSettings[] = await dwpSettingUtilsV2.getWorkspaceDeploymentConfigurations(rootFolder) ?? [];
const expectedDeploymentConfigurations = testCase.expectedVSCodeSettings?.deploymentConfigurations ?? [];
assert.strictEqual(deploymentConfigurationsV2.length, expectedDeploymentConfigurations.length, 'DeployWorkspaceProject ".vscode" saved settings mismatch.');

for (const [i, expectedDeploymentConfiguration] of expectedDeploymentConfigurations.entries()) {
const deploymentConfiguration: DeploymentConfigurationSettings = deploymentConfigurationsV2[i] ?? {};
assertStringPropsMatch(deploymentConfiguration as Partial<Record<string, string>>, expectedDeploymentConfiguration, 'DeployWorkspaceProject ".vscode" saved settings mismatch.');
}
for (const s of testScenarios) {
s.scenario = s.callback(setupTask);
}
});

// Verify 'postTestAssertion'
await testCase.postTestAssertion?.(context, results, 'DeployWorkspaceProject resource settings mismatch.');
});
});
});
}
for (const s of testScenarios) {
test(s.title, async function () {
await nonNullProp(s, 'scenario');
});
}
});

async function setupManagedEnvironment(): Promise<ManagedEnvironment | undefined> {
async function setupManagedEnvironment(): Promise<void> {
let managedEnvironment: ManagedEnvironment | undefined;
await runWithTestActionContext('createManagedEnvironment', async context => {
const resourceName: string = 'dwp' + randomUtils.getRandomHexString(6);
await context.ui.runWithInputs([resourceName, 'East US'], async () => {
managedEnvironment = await createManagedEnvironment(context);
try {
await runWithTestActionContext('createManagedEnvironment', async context => {
const resourceName: string = 'dwp' + randomUtils.getRandomHexString(6);
await context.ui.runWithInputs([resourceName, 'East US'], async () => {
managedEnvironment = await createManagedEnvironment(context);
});
});
});
return managedEnvironment;
}

function getMethodCleanWorkspaceFolderSettings(rootFolder: WorkspaceFolder) {
return async function cleanWorkspaceFolderSettings(): Promise<void> {
const settingsPath: string = settingUtils.getDefaultRootWorkspaceSettingsPath(rootFolder);
const vscodeFolderPath: string = path.dirname(settingsPath);
if (await AzExtFsExtra.pathExists(vscodeFolderPath)) {
await AzExtFsExtra.deleteResource(vscodeFolderPath, { recursive: true });
}
} catch (e) {
console.error(e);
}
}

assert.ok(managedEnvironment, 'Failed to create managed environment - skipping "deployWorkspaceProject" tests.');
resourceGroupsToDelete.add(nonNullProp(managedEnvironment, 'name'));
}
2 changes: 1 addition & 1 deletion test/nightly/deployWorkspaceProject/dwpTestScenarios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { generateAlbumApiJavaScriptTestCases } from "./testCases/albumApiJavaScr
import { type DeployWorkspaceProjectTestCase } from "./testCases/DeployWorkspaceProjectTestCase";
import { generateMonoRepoBasicTestCases } from "./testCases/monoRepoBasicTestCases";

interface DeployWorkspaceProjectTestScenario {
export interface DeployWorkspaceProjectTestScenario {
label: string;
folderName: string;
testCases: DeployWorkspaceProjectTestCase[];
Expand Down

0 comments on commit 874ff43

Please sign in to comment.