Skip to content

Commit 659dd51

Browse files
authored
Manually trigger creation of venvs to resolve flaky venv creation tests (#4041)
For #3848
1 parent ae1848a commit 659dd51

2 files changed

Lines changed: 55 additions & 27 deletions

File tree

src/client/interpreter/locators/services/workspaceVirtualEnvWatcherService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export class WorkspaceVirtualEnvWatcherService implements IInterpreterWatcher, D
5757
}
5858
}
5959
@traceDecorators.verbose('Intepreter Watcher change handler')
60-
protected async createHandler(e: Uri) {
60+
public async createHandler(e: Uri) {
6161
this.didCreate.fire();
6262
// On Windows, creation of environments are very slow, hence lets notify again after
6363
// the python executable is accessible (i.e. when we can launch the process).

src/test/interpreters/locators/workspaceVirtualEnvService.test.ts

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,22 @@ import { exec } from 'child_process';
99
import * as path from 'path';
1010
import { Uri } from 'vscode';
1111
import '../../../client/common/extensions';
12+
import { createDeferredFromPromise, Deferred } from '../../../client/common/utils/async';
13+
import { StopWatch } from '../../../client/common/utils/stopWatch';
1214
import {
13-
IInterpreterLocatorService, WORKSPACE_VIRTUAL_ENV_SERVICE
15+
IInterpreterLocatorService,
16+
IInterpreterWatcherBuilder,
17+
WORKSPACE_VIRTUAL_ENV_SERVICE
1418
} from '../../../client/interpreter/contracts';
19+
import { WorkspaceVirtualEnvWatcherService } from '../../../client/interpreter/locators/services/workspaceVirtualEnvWatcherService';
1520
import { IServiceContainer } from '../../../client/ioc/types';
16-
import {
17-
deleteFiles, isOs, isPythonVersionInProcess, OSType,
18-
PYTHON_PATH, rootWorkspaceUri, waitForCondition
19-
} from '../../common';
21+
import { deleteFiles, isPythonVersionInProcess, PYTHON_PATH, rootWorkspaceUri, waitForCondition } from '../../common';
2022
import { IS_MULTI_ROOT_TEST } from '../../constants';
21-
import { initialize, IS_VSTS, multirootPath } from '../../initialize';
23+
import { sleep } from '../../core';
24+
import { initialize, multirootPath } from '../../initialize';
2225

2326
const timeoutMs = 60_000;
24-
suite('Interpreters - Workspace VirtualEnv Service', function () {
27+
suite('Interpreters - Workspace VirtualEnv Service', function() {
2528
this.timeout(timeoutMs);
2629
this.retries(0);
2730

@@ -31,43 +34,65 @@ suite('Interpreters - Workspace VirtualEnv Service', function () {
3134
const venvPrefix = '.venv';
3235
let serviceContainer: IServiceContainer;
3336

37+
async function manuallyTriggerFSWatcher(deferred: Deferred<void>) {
38+
// Monitoring files on virtualized environments can be finicky...
39+
// Lets trigger the fs watcher manually for the tests.
40+
const stopWatch = new StopWatch();
41+
const builder = serviceContainer.get<IInterpreterWatcherBuilder>(IInterpreterWatcherBuilder);
42+
const watcher = (await builder.getWorkspaceVirtualEnvInterpreterWatcher(
43+
workspaceUri
44+
)) as WorkspaceVirtualEnvWatcherService;
45+
while (!deferred.completed && stopWatch.elapsedTime < timeoutMs - 10_000) {
46+
watcher.createHandler(workspaceUri).ignoreErrors();
47+
await sleep(1000);
48+
}
49+
}
3450
async function waitForInterpreterToBeDetected(envNameToLookFor: string) {
3551
const predicate = async () => {
3652
const items = await locator.getInterpreters(workspaceUri);
3753
return items.some(item => item.envName === envNameToLookFor);
3854
};
39-
await waitForCondition(predicate, timeoutMs, `${envNameToLookFor}, Environment not detected in the workspace ${workspaceUri.fsPath}`);
55+
const promise = waitForCondition(
56+
predicate,
57+
timeoutMs,
58+
`${envNameToLookFor}, Environment not detected in the workspace ${workspaceUri.fsPath}`
59+
);
60+
const deferred = createDeferredFromPromise(promise);
61+
manuallyTriggerFSWatcher(deferred).ignoreErrors();
62+
await deferred.promise;
4063
}
4164
async function createVirtualEnvironment(envSuffix: string) {
4265
// Ensure env is random to avoid conflicts in tests (currupting test data).
4366
const envName = `${venvPrefix}${envSuffix}${new Date().getTime().toString()}`;
4467
return new Promise<string>((resolve, reject) => {
45-
exec(`${PYTHON_PATH.fileToCommandArgument()} -m venv ${envName}`, { cwd: workspaceUri.fsPath }, (ex, _, stderr) => {
46-
if (ex) {
47-
return reject(ex);
48-
}
49-
if (stderr && stderr.length > 0) {
50-
reject(new Error(`Failed to create Env ${envName}, ${PYTHON_PATH}, Error: ${stderr}`));
51-
} else {
52-
resolve(envName);
68+
exec(
69+
`${PYTHON_PATH.fileToCommandArgument()} -m venv ${envName}`,
70+
{ cwd: workspaceUri.fsPath },
71+
(ex, _, stderr) => {
72+
if (ex) {
73+
return reject(ex);
74+
}
75+
if (stderr && stderr.length > 0) {
76+
reject(new Error(`Failed to create Env ${envName}, ${PYTHON_PATH}, Error: ${stderr}`));
77+
} else {
78+
resolve(envName);
79+
}
5380
}
54-
});
81+
);
5582
});
5683
}
5784

58-
suiteSetup(async function () {
59-
// skip for Linux CI, see #3848
60-
if (isOs(OSType.Linux) && IS_VSTS) {
61-
return this.skip();
62-
}
63-
85+
suiteSetup(async function() {
6486
// skip for Python < 3, no venv support
6587
if (await isPythonVersionInProcess(undefined, '2')) {
6688
return this.skip();
6789
}
6890

6991
serviceContainer = (await initialize()).serviceContainer;
70-
locator = serviceContainer.get<IInterpreterLocatorService>(IInterpreterLocatorService, WORKSPACE_VIRTUAL_ENV_SERVICE);
92+
locator = serviceContainer.get<IInterpreterLocatorService>(
93+
IInterpreterLocatorService,
94+
WORKSPACE_VIRTUAL_ENV_SERVICE
95+
);
7196
// This test is required, we need to wait for interpreter listing completes,
7297
// before proceeding with other tests.
7398
await deleteFiles(path.join(workspaceUri.fsPath, `${venvPrefix}*`));
@@ -91,15 +116,18 @@ suite('Interpreters - Workspace VirtualEnv Service', function () {
91116
await waitForInterpreterToBeDetected(env2);
92117
});
93118

94-
test('Detect a new Virtual Environment, and other workspace folder must not be affected (multiroot)', async function () {
119+
test('Detect a new Virtual Environment, and other workspace folder must not be affected (multiroot)', async function() {
95120
if (!IS_MULTI_ROOT_TEST) {
96121
return this.skip();
97122
}
98123
// There should be nothing in workspacec4.
99124
let items4 = await locator.getInterpreters(workspace4);
100125
expect(items4).to.be.lengthOf(0);
101126

102-
const [env1, env2] = await Promise.all([createVirtualEnvironment('first3'), createVirtualEnvironment('second3')]);
127+
const [env1, env2] = await Promise.all([
128+
createVirtualEnvironment('first3'),
129+
createVirtualEnvironment('second3')
130+
]);
103131
await Promise.all([waitForInterpreterToBeDetected(env1), waitForInterpreterToBeDetected(env2)]);
104132

105133
// Workspace4 should still not have any interpreters.

0 commit comments

Comments
 (0)