Skip to content

Commit daf2fc2

Browse files
authored
Fixes to detecting shells (microsoft#6627)
* Fixes * Better comments * Enable test
1 parent a7256df commit daf2fc2

File tree

8 files changed

+59
-14
lines changed

8 files changed

+59
-14
lines changed

src/client/common/terminal/shellDetector.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export class ShellDetector {
5353

5454
for (const detector of shellDetectors) {
5555
shell = detector.identify(telemetryProperties, terminal);
56+
traceVerbose(`${detector}. Shell identified as ${shell} ${terminal ? `(Terminal name is ${terminal.name})` : ''}`);
5657
if (shell) {
5758
break;
5859
}
@@ -66,6 +67,7 @@ export class ShellDetector {
6667

6768
// If we could not identify the shell, use the defaults.
6869
if (shell === undefined || shell === TerminalShellType.other) {
70+
traceVerbose('Using default OS shell');
6971
shell = defaultOSShells[this.platform.osType];
7072
}
7173
return shell;

src/client/common/terminal/shellDetectors/baseShellDetector.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { IShellDetector, ShellIdentificationTelemetry, TerminalShellType } from
1313
/*
1414
When identifying the shell use the following algorithm:
1515
* 1. Identify shell based on the name of the terminal (if there is one already opened and used).
16+
* 2. Identify shell based on the api provided by VSC.
1617
* 2. Identify shell based on the settings in VSC.
1718
* 3. Identify shell based on users environment variables.
1819
* 4. Use default shells (bash for mac and linux, cmd for windows).

src/client/common/terminal/shellDetectors/terminalNameShellDetector.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ import { BaseShellDetector } from './baseShellDetector';
1818
*/
1919
@injectable()
2020
export class TerminalNameShellDetector extends BaseShellDetector {
21-
constructor() { super(0); }
21+
constructor() { super(4); }
2222
public identify(telemetryProperties: ShellIdentificationTelemetry, terminal?: Terminal): TerminalShellType | undefined {
2323
if (!terminal) {
2424
return;
2525
}
26-
const shell = this.identifyShellFromShellPath(terminal.name)
26+
const shell = this.identifyShellFromShellPath(terminal.name);
2727
traceVerbose(`Terminal name '${terminal.name}' identified as shell '${shell}'`);
2828
telemetryProperties.shellIdentificationSource = shell === TerminalShellType.other ? telemetryProperties.shellIdentificationSource : 'terminalName';
2929
return shell;

src/client/common/terminal/shellDetectors/userEnvironmentShellDetector.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { OSType } from '../../utils/platform';
1212
import { ShellIdentificationTelemetry, TerminalShellType } from '../types';
1313
import { BaseShellDetector } from './baseShellDetector';
1414

15-
1615
/**
1716
* Identifies the shell based on the users environment (env variables).
1817
*
@@ -24,7 +23,7 @@ import { BaseShellDetector } from './baseShellDetector';
2423
export class UserEnvironmentShellDetector extends BaseShellDetector {
2524
constructor(@inject(ICurrentProcess) private readonly currentProcess: ICurrentProcess,
2625
@inject(IPlatformService) private readonly platform: IPlatformService) {
27-
super(3);
26+
super(1);
2827
}
2928
public getDefaultPlatformShell(): string {
3029
return getDefaultShell(this.platform, this.currentProcess);

src/client/common/terminal/shellDetectors/vscEnvironmentShellDetector.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { BaseShellDetector } from './baseShellDetector';
1818
* @extends {BaseShellDetector}
1919
*/
2020
export class VSCEnvironmentShellDetector extends BaseShellDetector {
21-
constructor(@inject(IApplicationEnvironment) private readonly appEnv: IApplicationEnvironment) { super(1); }
21+
constructor(@inject(IApplicationEnvironment) private readonly appEnv: IApplicationEnvironment) { super(3); }
2222
public identify(telemetryProperties: ShellIdentificationTelemetry, _terminal?: Terminal): TerminalShellType | undefined {
2323
if (!this.appEnv.shell) {
2424
return;

src/test/common/terminals/environmentActivationProviders/terminalActivation.testvirtualenvs.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,9 @@ suite('Activation of Environments in Terminal', () => {
129129
test('Should activate with virtualenv', async () => {
130130
await testActivation(envPaths.virtualEnvPath);
131131
});
132-
// test('Should activate with conda', async () => {
133-
// await terminalSettings.update('integrated.shell.windows', 'C:\\Windows\\System32\\cmd.exe', vscode.ConfigurationTarget.Global);
134-
// await pythonSettings.update('condaPath', envPaths.condaExecPath, vscode.ConfigurationTarget.Workspace);
135-
// await testActivation(envPaths.condaPath);
136-
// });
132+
test('Should activate with conda', async () => {
133+
await terminalSettings.update('integrated.shell.windows', 'C:\\Windows\\System32\\cmd.exe', vscode.ConfigurationTarget.Global);
134+
await pythonSettings.update('condaPath', envPaths.condaExecPath, vscode.ConfigurationTarget.Workspace);
135+
await testActivation(envPaths.condaPath);
136+
});
137137
});

src/test/common/terminals/shellDetector.unit.test.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,21 @@
44
'use strict';
55

66
import { expect } from 'chai';
7+
import * as sinon from 'sinon';
78
import { anything, instance, mock, verify, when } from 'ts-mockito';
9+
import { ApplicationEnvironment } from '../../../client/common/application/applicationEnvironment';
10+
import { WorkspaceService } from '../../../client/common/application/workspace';
811
import { PlatformService } from '../../../client/common/platform/platformService';
912
import { IPlatformService } from '../../../client/common/platform/types';
1013
import { ShellDetector } from '../../../client/common/terminal/shellDetector';
14+
import { SettingsShellDetector } from '../../../client/common/terminal/shellDetectors/settingsShellDetector';
15+
import { TerminalNameShellDetector } from '../../../client/common/terminal/shellDetectors/terminalNameShellDetector';
1116
import { UserEnvironmentShellDetector } from '../../../client/common/terminal/shellDetectors/userEnvironmentShellDetector';
17+
import { VSCEnvironmentShellDetector } from '../../../client/common/terminal/shellDetectors/vscEnvironmentShellDetector';
1218
import { TerminalShellType } from '../../../client/common/terminal/types';
1319
import { getNamesAndValues } from '../../../client/common/utils/enum';
1420
import { OSType } from '../../../client/common/utils/platform';
21+
import { MockProcess } from '../../../test/mocks/process';
1522

1623
// tslint:disable:max-func-body-length no-any
1724

@@ -23,11 +30,47 @@ suite('Shell Detector', () => {
2330
[OSType.Windows]: TerminalShellType.commandPrompt,
2431
[OSType.Unknown]: TerminalShellType.other
2532
};
26-
33+
const sandbox = sinon.createSandbox();
2734
setup(() => platformService = mock(PlatformService));
35+
teardown(() => sandbox.restore());
2836

2937
getNamesAndValues<OSType>(OSType).forEach(os => {
3038
const testSuffix = `(OS ${os.name})`;
39+
test('Test identification of Terminal Shells in order of priority', async () => {
40+
const callOrder: string[] = [];
41+
const nameDetectorIdentify = sandbox.stub(TerminalNameShellDetector.prototype, 'identify');
42+
nameDetectorIdentify.callsFake(() => {
43+
callOrder.push('calledFirst');
44+
return undefined;
45+
});
46+
const vscEnvDetectorIdentify = sandbox.stub(VSCEnvironmentShellDetector.prototype, 'identify');
47+
vscEnvDetectorIdentify.callsFake(() => {
48+
callOrder.push('calledSecond');
49+
return undefined;
50+
});
51+
const userEnvDetectorIdentify = sandbox.stub(UserEnvironmentShellDetector.prototype, 'identify');
52+
userEnvDetectorIdentify.callsFake(() => {
53+
callOrder.push('calledLast');
54+
return undefined;
55+
});
56+
const settingsDetectorIdentify = sandbox.stub(SettingsShellDetector.prototype, 'identify');
57+
settingsDetectorIdentify.callsFake(() => {
58+
callOrder.push('calledThird');
59+
return undefined;
60+
});
61+
62+
when(platformService.osType).thenReturn(os.value);
63+
const nameDetector = new TerminalNameShellDetector();
64+
const vscEnvDetector = new VSCEnvironmentShellDetector(instance(mock(ApplicationEnvironment)));
65+
const userEnvDetector = new UserEnvironmentShellDetector(mock(MockProcess), instance(platformService));
66+
const settingsDetector = new SettingsShellDetector(instance(mock(WorkspaceService)), instance(platformService));
67+
const detectors = [settingsDetector, userEnvDetector, nameDetector, vscEnvDetector];
68+
const shellDetector = new ShellDetector(instance(platformService), detectors);
69+
70+
shellDetector.identifyTerminalShell();
71+
72+
expect(callOrder).to.deep.equal(['calledFirst', 'calledSecond', 'calledThird', 'calledLast']);
73+
});
3174
test(`Use default shell based on OS if there are no shell detectors ${testSuffix}`, () => {
3275
when(platformService.osType).thenReturn(os.value);
3376
when(platformService.osType).thenReturn(os.value);

src/test/common/terminals/shellDetectors/shellDetectors.unit.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ suite('Shell Detectors', () => {
6363
appEnv = mock(ApplicationEnvironment);
6464
});
6565
test('Test Priority of detectors', async () => {
66-
expect(new TerminalNameShellDetector().priority).to.equal(0);
67-
expect(new VSCEnvironmentShellDetector(instance(appEnv)).priority).to.equal(1);
66+
expect(new TerminalNameShellDetector().priority).to.equal(4);
67+
expect(new VSCEnvironmentShellDetector(instance(appEnv)).priority).to.equal(3);
6868
expect(new SettingsShellDetector(instance(workspaceService), instance(platformService)).priority).to.equal(2);
69-
expect(new UserEnvironmentShellDetector(instance(currentProcess), instance(platformService)).priority).to.equal(3);
69+
expect(new UserEnvironmentShellDetector(instance(currentProcess), instance(platformService)).priority).to.equal(1);
7070
});
7171
test('Test identification of Terminal Shells (base class method)', async () => {
7272
const shellDetector = new TerminalNameShellDetector();

0 commit comments

Comments
 (0)