Skip to content

Commit b8e702b

Browse files
Kartik Rajkimadeline
Kartik Raj
andauthored
Add tests for environments collection (#17965)
* Add test for environments collection * Add test to handle updates from downstream locators * Add more tests * Fix tests * Minor stuff * Apply suggestions from code review Co-authored-by: Kim-Adeline Miguel <[email protected]> * Add test for missing branch Co-authored-by: Kim-Adeline Miguel <[email protected]>
1 parent 7d8cdca commit b8e702b

File tree

7 files changed

+427
-22
lines changed

7 files changed

+427
-22
lines changed

src/client/pythonEnvironments/base/info/env.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import { cloneDeep } from 'lodash';
55
import * as path from 'path';
6+
import { Uri } from 'vscode';
67
import { getArchitectureDisplayName } from '../../../common/platform/registry';
78
import { normalizeFilename } from '../../../common/utils/filesystem';
89
import { Architecture } from '../../../common/utils/platform';
@@ -29,6 +30,7 @@ export function buildEnvInfo(init?: {
2930
source?: PythonEnvSource[];
3031
display?: string;
3132
sysPrefix?: string;
33+
searchLocation?: Uri;
3234
}): PythonEnvInfo {
3335
const env = {
3436
name: init?.name ?? '',
@@ -90,6 +92,7 @@ function updateEnv(
9092
executable?: string;
9193
location?: string;
9294
version?: PythonVersion;
95+
searchLocation?: Uri;
9396
},
9497
): void {
9598
if (updates.kind !== undefined) {
@@ -104,6 +107,9 @@ function updateEnv(
104107
if (updates.version !== undefined) {
105108
env.version = updates.version;
106109
}
110+
if (updates.searchLocation !== undefined) {
111+
env.searchLocation = updates.searchLocation;
112+
}
107113
}
108114

109115
/**

src/client/pythonEnvironments/base/locators/composite/envsCollectionCache.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ export interface IEnvsCollectionCache {
5252
validateCache(): Promise<void>;
5353
}
5454

55-
type PythonEnvCompleteInfo = { hasCompleteInfo?: boolean } & PythonEnvInfo;
55+
export type PythonEnvCompleteInfo = { hasCompleteInfo?: boolean } & PythonEnvInfo;
5656

5757
interface IPersistentStorage {
58-
load(): Promise<PythonEnvInfo[] | undefined>;
58+
load(): Promise<PythonEnvInfo[]>;
5959
store(envs: PythonEnvInfo[]): Promise<void>;
6060
}
6161

@@ -118,7 +118,7 @@ export class PythonEnvInfoCache extends PythonEnvsWatcher<PythonEnvCollectionCha
118118
}
119119

120120
public async clearAndReloadFromStorage(): Promise<void> {
121-
this.envs = (await this.persistentStorage.load()) ?? this.envs;
121+
this.envs = await this.persistentStorage.load();
122122
}
123123

124124
public async flush(): Promise<void> {

src/client/pythonEnvironments/base/locators/composite/envsCollectionService.ts

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ export class EnvsCollectionService extends PythonEnvsWatcher<PythonEnvCollection
2121
/** Keeps track of ongoing refreshes for various queries. */
2222
private refreshPromises = new Map<PythonLocatorQuery | undefined, Promise<void>>();
2323

24-
/** Keeps track of whether there are any scheduled refreshes other than the ongoing one for various queries. */
25-
private scheduledRefreshes = new Map<PythonLocatorQuery | undefined, boolean>();
24+
/** Keeps track of scheduled refreshes other than the ongoing one for various queries. */
25+
private scheduledRefreshes = new Map<PythonLocatorQuery | undefined, Promise<void>>();
2626

2727
private readonly refreshStarted = new EventEmitter<void>();
2828

@@ -40,14 +40,14 @@ export class EnvsCollectionService extends PythonEnvsWatcher<PythonEnvCollection
4040
super();
4141
this.locator.onChanged((event) => {
4242
const query = undefined; // We can also form a query based on the event, but skip that for simplicity.
43-
const isNewRefreshScheduled = this.scheduledRefreshes.get(query);
44-
if (isNewRefreshScheduled) {
45-
// If there is already a new refresh scheduled for the query, no need to start another one.
46-
return;
43+
let scheduledRefresh = this.scheduledRefreshes.get(query);
44+
// If there is no refresh scheduled for the query, start a new one.
45+
if (!scheduledRefresh) {
46+
scheduledRefresh = this.scheduleNewRefresh(query);
4747
}
48-
this.scheduleNewRefresh(query).then(() => {
48+
scheduledRefresh.then(() => {
4949
// Once refresh of cache is complete, notify changes.
50-
this.fire({ type: event.type, searchLocation: event.searchLocation });
50+
this.fire(event);
5151
});
5252
});
5353
this.cache.onChanged((e) => {
@@ -73,7 +73,8 @@ export class EnvsCollectionService extends PythonEnvsWatcher<PythonEnvCollection
7373
public getEnvs(query?: PythonLocatorQuery): PythonEnvInfo[] {
7474
const cachedEnvs = this.cache.getAllEnvs();
7575
if (cachedEnvs.length === 0 && this.refreshPromises.size === 0) {
76-
traceError('Refresh should have already been triggered when activating discovery component');
76+
// We expect a refresh to already be triggered when activating discovery component.
77+
traceError('No python is installed or a refresh has not already been triggered');
7778
this.triggerRefresh().ignoreErrors();
7879
}
7980
return query ? cachedEnvs.filter(getQueryFilter(query)) : cachedEnvs;
@@ -159,13 +160,18 @@ export class EnvsCollectionService extends PythonEnvsWatcher<PythonEnvCollection
159160
* Ensure we trigger a fresh refresh for the query after the current refresh (if any) is done.
160161
*/
161162
private async scheduleNewRefresh(query?: PythonLocatorQuery): Promise<void> {
162-
this.scheduledRefreshes.set(query, true);
163-
const refreshPromise = this.getRefreshPromiseForQuery(query) ?? Promise.resolve();
164-
const nextRefreshPromise = refreshPromise.then(() => {
165-
// No more scheduled refreshes for this query as we're about to start the scheduled one.
166-
this.scheduledRefreshes.set(query, false);
167-
this.startRefresh(query);
168-
});
163+
const refreshPromise = this.getRefreshPromiseForQuery(query);
164+
let nextRefreshPromise: Promise<void>;
165+
if (!refreshPromise) {
166+
nextRefreshPromise = this.startRefresh(query);
167+
} else {
168+
nextRefreshPromise = refreshPromise.then(() => {
169+
// No more scheduled refreshes for this query as we're about to start the scheduled one.
170+
this.scheduledRefreshes.delete(query);
171+
this.startRefresh(query);
172+
});
173+
this.scheduledRefreshes.set(query, nextRefreshPromise);
174+
}
169175
return nextRefreshPromise;
170176
}
171177
}

src/client/pythonEnvironments/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ function createWorkspaceLocator(ext: ExtensionState): WorkspaceLocators<BasicEnv
161161
}
162162

163163
async function createCollectionCache(ext: ExtensionState): Promise<IEnvsCollectionCache> {
164-
const storage = getGlobalStorage<PythonEnvInfo[]>(ext.context, 'PYTHON_ENV_INFO_CACHE');
164+
const storage = getGlobalStorage<PythonEnvInfo[]>(ext.context, 'PYTHON_ENV_INFO_CACHE', []);
165165
const cache = await createCache({
166166
load: async () => storage.get(),
167167
store: async (e) => storage.set(e),

src/test/pythonEnvironments/base/common.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export class SimpleLocator<I = PythonEnvInfo> extends Locator<I> {
8787
public callbacks: {
8888
resolve?: null | ((env: PythonEnvInfo) => Promise<PythonEnvInfo | undefined>);
8989
before?: Promise<void>;
90-
after?: Promise<void>;
90+
after?(): Promise<void>;
9191
onUpdated?: Event<PythonEnvUpdatedEvent<I> | null>;
9292
beforeEach?(e: I): Promise<void>;
9393
afterEach?(e: I): Promise<void>;
@@ -137,7 +137,7 @@ export class SimpleLocator<I = PythonEnvInfo> extends Locator<I> {
137137
}
138138
}
139139
if (callbacks?.after !== undefined) {
140-
await callbacks.after;
140+
await callbacks.after();
141141
}
142142
deferred.resolve();
143143
})();

0 commit comments

Comments
 (0)