Skip to content

Commit 5314a97

Browse files
author
Kartik Raj
committed
Temp
1 parent bad31ec commit 5314a97

File tree

2 files changed

+199
-1
lines changed

2 files changed

+199
-1
lines changed

src/client/pythonEnvironments/collection/environmentsResolver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
import { PythonEnvsChangedEvent } from '../base/watcher';
1313
import { IEnvironmentInfoService } from '../info/environmentInfoService';
1414

15-
export class PythonEnvsReducer implements ILocator {
15+
export class PythonEnvsResolver implements ILocator {
1616
public get onChanged(): Event<PythonEnvsChangedEvent> {
1717
return this.pythonEnvsReducer.onChanged;
1818
}
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
import { assert, expect } from 'chai';
5+
import * as path from 'path';
6+
import { ImportMock } from 'ts-mock-imports';
7+
import { EventEmitter } from 'vscode';
8+
import { ExecutionResult } from '../../../client/common/process/types';
9+
import { PythonEnvInfo, PythonEnvKind } from '../../../client/pythonEnvironments/base/info';
10+
import { PythonEnvUpdatedEvent } from '../../../client/pythonEnvironments/base/locator';
11+
import { PythonEnvsChangedEvent } from '../../../client/pythonEnvironments/base/watcher';
12+
import { PythonEnvsResolver } from '../../../client/pythonEnvironments/collection/environmentsResolver';
13+
import * as ExternalDep from '../../../client/pythonEnvironments/common/externalDependencies';
14+
import { EnvironmentInfoService, IEnvironmentInfoService } from '../../../client/pythonEnvironments/info/environmentInfoService';
15+
import { sleep } from '../../core';
16+
import { createEnv, getEnvs, SimpleLocator } from '../base/common';
17+
18+
suite('Environments Reducer', () => {
19+
suite('iterEnvs()', () => {
20+
let stubShellExec: sinon.SinonStub;
21+
let envService: IEnvironmentInfoService;
22+
23+
setup(() => {
24+
envService = new EnvironmentInfoService();
25+
stubShellExec = ImportMock.mockFunction(
26+
ExternalDep,
27+
'shellExecute',
28+
new Promise<ExecutionResult<string>>((resolve) => {
29+
resolve({
30+
stdout:
31+
'{"versionInfo": [3, 8, 3, "final", 0], "sysPrefix": "path", "version": "3.8.3 (tags/v3.8.3:6f8c832, May 13 2020, 22:37:02) [MSC v.1924 64 bit (AMD64)]", "is64Bit": true}',
32+
});
33+
}),
34+
);
35+
});
36+
teardown(() => {
37+
stubShellExec.restore();
38+
});
39+
40+
test('Iterator only yields unique environments', async () => {
41+
const env1 = createEnv('env1', '3.5.12b1', PythonEnvKind.Venv, path.join('path', 'to', 'exec1'));
42+
const env2 = createEnv('env2', '3.8.1', PythonEnvKind.Conda, path.join('path', 'to', 'exec2'));
43+
const env3 = createEnv('env3', '2.7', PythonEnvKind.System, path.join('path', 'to', 'exec3'));
44+
const env4 = createEnv('env4', '3.9.0rc2', PythonEnvKind.Unknown, path.join('path', 'to', 'exec2')); // Same as env2
45+
const env5 = createEnv('env5', '3.8', PythonEnvKind.Venv, path.join('path', 'to', 'exec1')); // Same as env1
46+
const environmentsToBeIterated = [env1, env2, env3, env4, env5]; // Contains 3 unique environments
47+
const pythonEnvManager = new SimpleLocator(environmentsToBeIterated);
48+
const reducer = new PythonEnvsResolver(pythonEnvManager, envService);
49+
50+
const iterator = reducer.iterEnvs();
51+
const envs = await getEnvs(iterator);
52+
53+
const expected = [env1, env2, env3];
54+
assert.deepEqual(envs, expected);
55+
});
56+
57+
test('Single updates for multiple environments are sent correctly followed by the null event', async () => {
58+
// Arrange
59+
const env1 = createEnv('env1', '3.5.12b1', PythonEnvKind.Unknown, path.join('path', 'to', 'exec1'));
60+
const env2 = createEnv('env2', '3.8.1', PythonEnvKind.Unknown, path.join('path', 'to', 'exec2'));
61+
const env3 = createEnv('env3', '2.7', PythonEnvKind.System, path.join('path', 'to', 'exec3'));
62+
const env4 = createEnv('env4', '3.9.0rc2', PythonEnvKind.Conda, path.join('path', 'to', 'exec2')); // Same as env2;
63+
const env5 = createEnv('env5', '3.8', PythonEnvKind.Venv, path.join('path', 'to', 'exec1')); // Same as env1;
64+
const environmentsToBeIterated = [env1, env2, env3, env4, env5]; // Contains 3 unique environments
65+
const pythonEnvManager = new SimpleLocator(environmentsToBeIterated);
66+
const onUpdatedEvents: (PythonEnvUpdatedEvent | null)[] = [];
67+
const reducer = new PythonEnvsResolver(pythonEnvManager, envService);
68+
69+
const iterator = reducer.iterEnvs(); // Act
70+
71+
// Assert
72+
let { onUpdated } = iterator;
73+
expect(onUpdated).to.not.equal(undefined, '');
74+
75+
// Arrange
76+
onUpdated = onUpdated!;
77+
onUpdated((e) => {
78+
onUpdatedEvents.push(e);
79+
});
80+
81+
// Act
82+
await getEnvs(iterator);
83+
await sleep(1); // Resolve pending calls in the background
84+
85+
// Assert
86+
const expectedUpdates = [
87+
{ old: env2, new: mergeEnvironments(env2, env4) },
88+
{ old: env1, new: mergeEnvironments(env1, env5) },
89+
null,
90+
];
91+
assert.deepEqual(expectedUpdates, onUpdatedEvents);
92+
});
93+
94+
test('Multiple updates for the same environment are sent correctly followed by the null event', async () => {
95+
// Arrange
96+
const env1 = createEnv('env1', '3.8', PythonEnvKind.Unknown, path.join('path', 'to', 'exec'));
97+
const env2 = createEnv('env2', '3.8.1', PythonEnvKind.System, path.join('path', 'to', 'exec'));
98+
const env3 = createEnv('env3', '3.8.1', PythonEnvKind.Conda, path.join('path', 'to', 'exec'));
99+
const environmentsToBeIterated = [env1, env2, env3]; // All refer to the same environment
100+
const pythonEnvManager = new SimpleLocator(environmentsToBeIterated);
101+
const onUpdatedEvents: (PythonEnvUpdatedEvent | null)[] = [];
102+
const reducer = new PythonEnvsResolver(pythonEnvManager, envService);
103+
104+
const iterator = reducer.iterEnvs(); // Act
105+
106+
// Assert
107+
let { onUpdated } = iterator;
108+
expect(onUpdated).to.not.equal(undefined, '');
109+
110+
// Arrange
111+
onUpdated = onUpdated!;
112+
onUpdated((e) => {
113+
onUpdatedEvents.push(e);
114+
});
115+
116+
// Act
117+
await getEnvs(iterator);
118+
await sleep(1); // Resolve pending calls in the background
119+
120+
// Assert
121+
const env12 = mergeEnvironments(env1, env2);
122+
const expectedUpdates = [
123+
{ old: env1, new: env12 },
124+
{ old: env12, new: mergeEnvironments(env12, env3) },
125+
null,
126+
];
127+
assert.deepEqual(expectedUpdates, onUpdatedEvents);
128+
});
129+
130+
test('Updates to environments from the incoming iterator are passed on correctly followed by the null event', async () => {
131+
// Arrange
132+
const env1 = createEnv('env1', '3.8', PythonEnvKind.Unknown, path.join('path', 'to', 'exec'));
133+
const env2 = createEnv('env2', '3.8.1', PythonEnvKind.System, path.join('path', 'to', 'exec'));
134+
const environmentsToBeIterated = [env1];
135+
const didUpdate = new EventEmitter<PythonEnvUpdatedEvent | null>();
136+
const pythonEnvManager = new SimpleLocator(environmentsToBeIterated, { onUpdated: didUpdate.event });
137+
const onUpdatedEvents: (PythonEnvUpdatedEvent | null)[] = [];
138+
const reducer = new PythonEnvsResolver(pythonEnvManager, envService);
139+
140+
const iterator = reducer.iterEnvs(); // Act
141+
142+
// Assert
143+
let { onUpdated } = iterator;
144+
expect(onUpdated).to.not.equal(undefined, '');
145+
146+
// Arrange
147+
onUpdated = onUpdated!;
148+
onUpdated((e) => {
149+
onUpdatedEvents.push(e);
150+
});
151+
152+
// Act
153+
await getEnvs(iterator);
154+
didUpdate.fire({ old: env1, new: env2 });
155+
didUpdate.fire(null); // It is essential for the incoming iterator to fire "null" event signifying it's done
156+
await sleep(1);
157+
158+
// Assert
159+
const expectedUpdates = [{ old: env1, new: mergeEnvironments(env1, env2) }, null];
160+
assert.deepEqual(expectedUpdates, onUpdatedEvents);
161+
didUpdate.dispose();
162+
});
163+
});
164+
165+
test('onChanged fires iff onChanged from reducer fires', () => {
166+
const pythonEnvManager = new SimpleLocator([]);
167+
const event1: PythonEnvsChangedEvent = {};
168+
const event2: PythonEnvsChangedEvent = { kind: PythonEnvKind.Unknown };
169+
const expected = [event1, event2];
170+
const reducer = new PythonEnvsResolver(pythonEnvManager, new EnvironmentInfoService());
171+
172+
const events: PythonEnvsChangedEvent[] = [];
173+
reducer.onChanged((e) => events.push(e));
174+
175+
pythonEnvManager.fire(event1);
176+
pythonEnvManager.fire(event2);
177+
178+
assert.deepEqual(events, expected);
179+
});
180+
181+
test('Calls reducer to resolves environments', async () => {
182+
const env = createEnv('env1', '3.8', PythonEnvKind.Unknown, path.join('path', 'to', 'exec'));
183+
const resolvedEnv = createEnv('env1', '3.8.1', PythonEnvKind.Conda, 'resolved/path/to/exec');
184+
const pythonEnvManager = new SimpleLocator([], {
185+
resolve: async (e: PythonEnvInfo) => {
186+
if (e === env) {
187+
return resolvedEnv;
188+
}
189+
return undefined;
190+
},
191+
});
192+
const reducer = new PythonEnvsResolver(pythonEnvManager, new EnvironmentInfoService());
193+
194+
const expected = await reducer.resolveEnv(env);
195+
196+
assert.deepEqual(expected, resolvedEnv);
197+
});
198+
});

0 commit comments

Comments
 (0)