Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@
"--ui=tdd",
"--recursive",
"--colors",
//"--grep", "<suite name>",
// "--grep", "<suite>",
"--timeout=300000",
"--exit"
],
Expand Down
11 changes: 6 additions & 5 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -561,9 +561,10 @@
"DataScienceRendererExtension.downloadCompletedOutputMessage": "Notebook Renderers extension download complete.",
"DataScience.uriProviderDescriptionFormat": "{0} (From {1} extension)",
"DataScience.unknownPackage": "unknown",
"DataScience.interactiveWindowTitleFormat": "Python Interactive - {0}",
"DataScience.interactiveWindowModeBannerTitle": "Do you want to open a new Python Interactive window for this file? [More Information](command:workbench.action.openSettings?%5B%22python.dataScience.interactiveWindowMode%22%5D).",
"DataScience.interactiveWindowModeBannerSwitchYes": "Yes",
"DataScience.interactiveWindowModeBannerSwitchAlways": "Always",
"DataScience.interactiveWindowModeBannerSwitchNo": "No"
"DataScience.interactiveWindowTitle" : "Python Interactive",
"DataScience.interactiveWindowTitleFormat" : "Python Interactive - {0}",
"DataScience.interactiveWindowModeBannerTitle" : "Do you want to open a new Python Interactive window for this file? [More Information](command:workbench.action.openSettings?%5B%22python.dataScience.interactiveWindowMode%22%5D).",
"DataScience.interactiveWindowModeBannerSwitchYes" : "Yes",
"DataScience.interactiveWindowModeBannerSwitchAlways" : "Always",
"DataScience.interactiveWindowModeBannerSwitchNo" : "No"
}
2 changes: 1 addition & 1 deletion src/client/common/utils/localize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ export namespace DataScience {
'{0} (From {1} extension)'
);
export const unknownPackage = localize('DataScience.unknownPackage', 'unknown');
export const interactiveWindowTitle = localize('DataScience.interactiveWindowTitleFormat', 'Python Interactive');
export const interactiveWindowTitle = localize('DataScience.interactiveWindowTitle', 'Python Interactive');
export const interactiveWindowTitleFormat = localize(
'DataScience.interactiveWindowTitleFormat',
'Python Interactive - {0}'
Expand Down
16 changes: 11 additions & 5 deletions src/client/datascience/jupyter/jupyterNotebook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,13 +225,19 @@ export class JupyterNotebookBase implements INotebook {
this.sessionStatusChanged.dispose();
this.onStatusChangedEvent = undefined;
}

traceInfo(`Shutting down session ${this.identity.toString()}`);
if (this.session) {
await this.session.dispose().catch(traceError.bind('Failed to dispose session from JupyterNotebook'));
}
this.loggers.forEach((d) => d.dispose());
this.disposed.fire();

try {
traceInfo(`Shutting down session ${this.identity.toString()}`);
if (this.session) {
await this.session
.dispose()
.catch(traceError.bind('Failed to dispose session from JupyterNotebook'));
}
} catch (exc) {
traceError(`Exception shutting down session `, exc);
}
}
}

Expand Down
76 changes: 48 additions & 28 deletions src/test/datascience/dataScienceIocContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { interfaces } from 'inversify';
import * as os from 'os';
import * as path from 'path';
import { SemVer } from 'semver';
import { anything, instance, mock, reset, when } from 'ts-mockito';
import { anyString, anything, instance, mock, reset, when } from 'ts-mockito';
import * as TypeMoq from 'typemoq';
import {
CancellationTokenSource,
Expand All @@ -20,6 +20,7 @@ import {
FileSystemWatcher,
Memento,
Uri,
WindowState,
WorkspaceFolder,
WorkspaceFoldersChangeEvent
} from 'vscode';
Expand Down Expand Up @@ -68,6 +69,7 @@ import {
IDiagnosticsService
} from '../../client/application/diagnostics/types';
import { ApplicationEnvironment } from '../../client/common/application/applicationEnvironment';
import { ApplicationShell } from '../../client/common/application/applicationShell';
import { ClipboardService } from '../../client/common/application/clipboard';
import { VSCodeNotebook } from '../../client/common/application/notebook';
import { TerminalManager } from '../../client/common/application/terminalManager';
Expand Down Expand Up @@ -158,6 +160,7 @@ import {
ICryptoUtils,
ICurrentProcess,
IDataScienceSettings,
IDisposable,
IExperimentService,
IExperimentsManager,
IExtensionContext,
Expand Down Expand Up @@ -424,7 +427,7 @@ export class DataScienceIocContainer extends UnitTestIocContainer {
}
private static jupyterInterpreters: PythonInterpreter[] = [];
private static foundPythonPath: string | undefined;
public applicationShell!: TypeMoq.IMock<IApplicationShell>;
public applicationShell!: ApplicationShell;
// tslint:disable-next-line:no-any
public datascience!: TypeMoq.IMock<IDataScience>;
public shouldMockJupyter: boolean;
Expand Down Expand Up @@ -938,7 +941,7 @@ export class DataScienceIocContainer extends UnitTestIocContainer {
this.serviceManager.addSingletonInstance<ICommandManager>(ICommandManager, this.commandManager);

// Mock the app shell
const appShell = (this.applicationShell = TypeMoq.Mock.ofType<IApplicationShell>());
this.applicationShell = mock(ApplicationShell);
const configurationService = TypeMoq.Mock.ofType<IConfigurationService>();

configurationService.setup((c) => c.getSettings(TypeMoq.It.isAny())).returns(this.getSettings.bind(this));
Expand All @@ -948,7 +951,7 @@ export class DataScienceIocContainer extends UnitTestIocContainer {
EnvironmentVariablesProvider
);

this.serviceManager.addSingletonInstance<IApplicationShell>(IApplicationShell, appShell.object);
this.serviceManager.addSingletonInstance<IApplicationShell>(IApplicationShell, instance(this.applicationShell));
this.serviceManager.addSingleton<IClipboard>(IClipboard, ClipboardService);
this.serviceManager.addSingletonInstance<IDocumentManager>(IDocumentManager, this.documentManager);
this.serviceManager.addSingletonInstance<IConfigurationService>(
Expand Down Expand Up @@ -1131,35 +1134,52 @@ export class DataScienceIocContainer extends UnitTestIocContainer {
}
};

appShell.setup((a) => a.showErrorMessage(TypeMoq.It.isAnyString())).returns(() => Promise.resolve(''));
appShell
.setup((a) => a.showErrorMessage(TypeMoq.It.isAnyString(), anything()))
.returns(() => Promise.resolve(''));
appShell
.setup((a) => a.showErrorMessage(TypeMoq.It.isAnyString(), anything(), anything()))
.returns(() => Promise.resolve(''));
appShell
.setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.returns(() => Promise.resolve(''));
appShell
.setup((a) => a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.returns((_a1: string, a2: string, _a3: string) => Promise.resolve(a2));
appShell
.setup((a) =>
a.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())
)
.returns((_a1: string, a2: any, _a3: string, a4: string) => {
when(this.applicationShell.showErrorMessage(anyString())).thenReturn(Promise.resolve(''));
when(this.applicationShell.showErrorMessage(anyString(), anything())).thenReturn(Promise.resolve(''));
when(this.applicationShell.showErrorMessage(anyString(), anything(), anything())).thenReturn(
Promise.resolve('')
);
when(this.applicationShell.showInformationMessage(anyString())).thenReturn(Promise.resolve(''));
when(this.applicationShell.showInformationMessage(anyString(), anything())).thenReturn(Promise.resolve(''));
when(
this.applicationShell.showInformationMessage(anyString(), anything(), anything())
).thenCall((_a1, a2, _a3) => Promise.resolve(a2));
when(this.applicationShell.showInformationMessage(anyString(), anything(), anything(), anything())).thenCall(
(_a1, a2, _a3, a4) => {
if (typeof a2 === 'string') {
return Promise.resolve(a2);
} else {
return Promise.resolve(a4);
}
});
appShell
.setup((a) => a.showSaveDialog(TypeMoq.It.isAny()))
.returns(() => Promise.resolve(Uri.file('test.ipynb')));
appShell.setup((a) => a.setStatusBarMessage(TypeMoq.It.isAny())).returns(() => dummyDisposable);
appShell.setup((a) => a.showInputBox(TypeMoq.It.isAny())).returns(() => Promise.resolve('Python'));
}
);
when(this.applicationShell.showWarningMessage(anyString())).thenReturn(Promise.resolve(''));
when(this.applicationShell.showWarningMessage(anyString(), anything())).thenReturn(Promise.resolve(''));
when(this.applicationShell.showWarningMessage(anyString(), anything(), anything())).thenCall((_a1, a2, _a3) =>
Promise.resolve(a2)
);
when(this.applicationShell.showWarningMessage(anyString(), anything(), anything(), anything())).thenCall(
(_a1, a2, _a3, a4) => {
if (typeof a2 === 'string') {
return Promise.resolve(a2);
} else {
return Promise.resolve(a4);
}
}
);
when(this.applicationShell.showSaveDialog(anything())).thenReturn(Promise.resolve(Uri.file('test.ipynb')));
when(this.applicationShell.setStatusBarMessage(anything())).thenReturn(dummyDisposable);
when(this.applicationShell.showInputBox(anything())).thenReturn(Promise.resolve('Python'));
const eventCallback = (
_listener: (e: WindowState) => any,
_thisArgs?: any,
_disposables?: IDisposable[] | Disposable
) => {
return {
dispose: noop
};
};
when(this.applicationShell.onDidChangeWindowState).thenReturn(eventCallback);

const interpreterManager = this.serviceContainer.get<IInterpreterService>(IInterpreterService);
interpreterManager.initialize();
Expand Down
21 changes: 9 additions & 12 deletions src/test/datascience/interactiveWindow.functional.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,8 @@ for i in range(0, 100):
);

// Then open a second time and make sure it uses this new path
const secondPath = await interpreterService.getActiveInterpreter(secondUri);
assert.notEqual(secondPath?.path, activeInterpreter?.path, 'Second path was not set');
const newWindow = (await interactiveWindowProvider.getOrCreate(undefined)) as InteractiveWindow;
await addCode(ioc, 'a=1\na', false, secondUri);
assert.notEqual(
Expand Down Expand Up @@ -1237,12 +1239,12 @@ for i in range(0, 100):
const lenses = fooWatcher?.getCodeLenses();
assert.equal(lenses?.length, 6, 'No code lenses found');
await runCodeLens(fooWatcher!.uri!, lenses ? lenses[0] : undefined, ioc);
verifyHtmlOnCell(pair1.mount.wrapper, 'InteractiveCell', '<span>foo</span>', CellPosition.Last);
verifyHtmlOnCell(pair1.mount.wrapper, 'InteractiveCell', 'foo', CellPosition.Last);

// Create another window, run a cell again
const pair2 = await getOrCreateInteractiveWindow(ioc);
await runCodeLens(fooWatcher!.uri!, lenses ? lenses[0] : undefined, ioc);
verifyHtmlOnCell(pair2.mount.wrapper, 'InteractiveCell', '<span>foo</span>', CellPosition.Last);
verifyHtmlOnCell(pair2.mount.wrapper, 'InteractiveCell', 'foo', CellPosition.Last);

// Make the first window active
pair2.mount.changeViewState(false, false);
Expand All @@ -1253,7 +1255,7 @@ for i in range(0, 100):
const lenses2 = barWatcher?.getCodeLenses();
assert.equal(lenses2?.length, 6, 'No code lenses found');
await runCodeLens(barWatcher!.uri!, lenses2 ? lenses2[0] : undefined, ioc);
verifyHtmlOnCell(pair1.mount.wrapper, 'InteractiveCell', '<span>bar</span>', CellPosition.Last);
verifyHtmlOnCell(pair1.mount.wrapper, 'InteractiveCell', 'bar', CellPosition.Last);
});
test('Per file', async () => {
addMockData(ioc, fooCode, 'foo');
Expand All @@ -1268,7 +1270,7 @@ for i in range(0, 100):
await runCodeLens(fooWatcher!.uri!, lenses ? lenses[0] : undefined, ioc);
assert.equal(interactiveWindowProvider.windows.length, 1, 'Interactive window not created');
const mounted1 = interactiveWindowProvider.getMountedWebView(interactiveWindowProvider.windows[0]);
verifyHtmlOnCell(mounted1.wrapper, 'InteractiveCell', '<span>foo</span>', CellPosition.Last);
verifyHtmlOnCell(mounted1.wrapper, 'InteractiveCell', 'foo', CellPosition.Last);

// Create another window, run a cell again
const barWatcher = createCodeWatcher(`# %%\n${barCode}`, 'bar.py', ioc);
Expand All @@ -1278,16 +1280,11 @@ for i in range(0, 100):
const mounted2 = interactiveWindowProvider.getMountedWebView(
interactiveWindowProvider.windows.find((w) => w.title.includes('bar'))
);
verifyHtmlOnCell(mounted2.wrapper, 'InteractiveCell', '<span>bar</span>', CellPosition.Last);
verifyHtmlOnCell(mounted2.wrapper, 'InteractiveCell', 'bar', CellPosition.Last);
});
test('Per file asks and changes titles', async () => {
addMockData(ioc, fooCode, 'foo');
addMockData(ioc, barCode, 'bar');
ioc.applicationShell
.setup((i) => i.showInformationMessage(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.returns((_a1: string, a2: string, _a3: string) => {
return Promise.resolve(a2);
});
ioc.forceDataScienceSettingsChanged({ interactiveWindowMode: 'multiple' });
const interactiveWindowProvider = ioc.get<ITestInteractiveWindowProvider>(IInteractiveWindowProvider);
const globalMemento = ioc.get<Memento>(IMemento, GLOBAL_MEMENTO);
Expand All @@ -1300,7 +1297,7 @@ for i in range(0, 100):
await runCodeLens(fooWatcher!.uri!, lenses ? lenses[0] : undefined, ioc);
assert.equal(interactiveWindowProvider.windows.length, 1, 'Interactive window not created');
const mounted1 = interactiveWindowProvider.getMountedWebView(interactiveWindowProvider.windows[0]);
verifyHtmlOnCell(mounted1.wrapper, 'InteractiveCell', '<span>foo</span>', CellPosition.Last);
verifyHtmlOnCell(mounted1.wrapper, 'InteractiveCell', 'foo', CellPosition.Last);

// Create another window, run a cell again
const barWatcher = createCodeWatcher(`# %%\n${barCode}`, 'bar.py', ioc);
Expand All @@ -1310,7 +1307,7 @@ for i in range(0, 100):
const mounted2 = interactiveWindowProvider.getMountedWebView(
interactiveWindowProvider.windows.find((w) => w.title.includes('bar'))
);
verifyHtmlOnCell(mounted2.wrapper, 'InteractiveCell', '<span>bar</span>', CellPosition.Last);
verifyHtmlOnCell(mounted2.wrapper, 'InteractiveCell', 'bar', CellPosition.Last);

// First window should now have foo in the title too
assert.ok(interactiveWindowProvider.windows[0].title.includes('foo'), 'Title of first window did not change');
Expand Down
19 changes: 15 additions & 4 deletions src/test/datascience/nativeEditor.functional.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as chaiAsPromised from 'chai-as-promised';
import * as dedent from 'dedent';
import { ReactWrapper } from 'enzyme';
import * as fs from 'fs-extra';
import { IDisposable } from 'monaco-editor';
import * as path from 'path';
import * as sinon from 'sinon';
import { anything, objectContaining, when } from 'ts-mockito';
Expand Down Expand Up @@ -903,7 +904,9 @@ df.head()`;
await promise;
}
}
await ioc.dispose();
if (ioc) {
await ioc.dispose();
}
try {
notebookFile.cleanupCallback();
} catch {
Expand Down Expand Up @@ -2074,11 +2077,19 @@ df.head()`;
}
await initIoc();

const eventCallback = (
listener: (e: WindowState) => any,
_thisArgs?: any,
_disposables?: IDisposable[] | Disposable
) => {
windowStateChangeHandlers.push(listener);
return {
dispose: noop
};
};
windowStateChangeHandlers = [];
// Keep track of all handlers for the onDidChangeWindowState event.
ioc.applicationShell
.setup((app) => app.onDidChangeWindowState(TypeMoq.It.isAny()))
.callback((cb) => windowStateChangeHandlers.push(cb));
when(ioc.applicationShell.onDidChangeWindowState).thenReturn(eventCallback);

// tslint:disable-next-line: no-invalid-this
await setupFunction.call(this);
Expand Down
Loading