From 5f0f06fe2364ccd09c57eae922cf6e50f4cf2348 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Wed, 2 Oct 2019 13:22:12 -0700 Subject: [PATCH 1/4] Change variable explorer tests to wait for variable explorer complete --- .../interactiveWindowTypes.ts | 2 + .../interactive-common/mainStateController.ts | 7 ++++ .../variableexplorer.functional.test.tsx | 38 +++++++++++++------ 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/client/datascience/interactive-common/interactiveWindowTypes.ts b/src/client/datascience/interactive-common/interactiveWindowTypes.ts index 59b3be5b9fae..446563bcb4de 100644 --- a/src/client/datascience/interactive-common/interactiveWindowTypes.ts +++ b/src/client/datascience/interactive-common/interactiveWindowTypes.ts @@ -69,6 +69,7 @@ export namespace InteractiveWindowMessages { export const NotebookClean = 'clean'; export const SaveAll = 'save_all'; export const NativeCommand = 'native_command'; + export const VariablesComplete = 'variables_complete'; } @@ -301,4 +302,5 @@ export class IInteractiveWindowMapping { public [InteractiveWindowMessages.NotebookClean]: never | undefined; public [InteractiveWindowMessages.SaveAll]: ISaveAll; public [InteractiveWindowMessages.NativeCommand]: INativeCommand; + public [InteractiveWindowMessages.VariablesComplete]: never | undefined; } diff --git a/src/datascience-ui/interactive-common/mainStateController.ts b/src/datascience-ui/interactive-common/mainStateController.ts index c766d2ae2947..9f71d2be2c5c 100644 --- a/src/datascience-ui/interactive-common/mainStateController.ts +++ b/src/datascience-ui/interactive-common/mainStateController.ts @@ -636,6 +636,8 @@ export class MainStateController implements IMessageHandler { } public renderUpdate(newState: {}) { + const oldCount = this.state.pendingVariableCount; + // This method should be called during the render stage of anything // using this state Controller. That's because after shouldComponentUpdate // render is next and at this point the state has been set. @@ -648,6 +650,11 @@ export class MainStateController implements IMessageHandler { if ('cellVMs' in newState) { this.sendInfo(); } + + // If the new state includes pendingVariableCount and it's gone to zero, send a message + if ('pendingVariableCount' in newState && this.state.pendingVariableCount === 0 && oldCount !== 0) { + setTimeout(() => this.sendMessage(InteractiveWindowMessages.VariablesComplete), 1); + } } public getState(): IMainState { diff --git a/src/test/datascience/variableexplorer.functional.test.tsx b/src/test/datascience/variableexplorer.functional.test.tsx index 55c230c0f265..7c58f8c5528d 100644 --- a/src/test/datascience/variableexplorer.functional.test.tsx +++ b/src/test/datascience/variableexplorer.functional.test.tsx @@ -8,6 +8,7 @@ import { parse } from 'node-html-parser'; import * as React from 'react'; import { Disposable } from 'vscode'; +import { InteractiveWindowMessages } from '../../client/datascience/interactive-common/interactiveWindowTypes'; import { IJupyterVariable } from '../../client/datascience/types'; import { InteractivePanel } from '../../datascience-ui/history-react/interactivePanel'; import { VariableExplorer } from '../../datascience-ui/interactive-common/variableExplorer'; @@ -16,7 +17,7 @@ import { DataScienceIocContainer } from './dataScienceIocContainer'; import { addCode } from './interactiveWindowTestHelpers'; import { addCell, createNewEditor } from './nativeEditorTestHelpers'; import { waitForUpdate } from './reactHelpers'; -import { runDoubleTest } from './testHelpers'; +import { runDoubleTest, waitForMessage } from './testHelpers'; // tslint:disable:max-func-body-length trailing-comma no-any no-multiline-string suite('DataScience Interactive Window variable explorer tests', () => { @@ -60,6 +61,10 @@ suite('DataScience Interactive Window variable explorer tests', () => { // asyncDump(); //}); + async function waitForVariablesUpdated(): Promise { + return waitForMessage(ioc, InteractiveWindowMessages.VariablesComplete); + } + async function addCodeImpartial(wrapper: ReactWrapper, React.Component>, code: string, expectedRenderCount: number = 4, expectError: boolean = false): Promise, React.Component>> { const nodes = wrapper.find('InteractivePanel'); if (nodes.length > 0) { @@ -85,8 +90,9 @@ value = 'hello world'`; openVariableExplorer(wrapper); await addCodeImpartial(wrapper, 'a=1\na'); + let updated = waitForVariablesUpdated(); await addCodeImpartial(wrapper, basicCode, 4); - await waitForUpdate(wrapper, VariableExplorer, 3); + await updated; // We should show a string and show an int, the modules should be hidden let targetVariables: IJupyterVariable[] = [ @@ -100,8 +106,9 @@ value = 'hello world'`; ioc.getSettings().datascience.variableExplorerExclude = `${ioc.getSettings().datascience.variableExplorerExclude};str`; // Add another string and check our vars, strings should be hidden + updated = waitForVariablesUpdated(); await addCodeImpartial(wrapper, basicCode2, 4); - await waitForUpdate(wrapper, VariableExplorer, 2); + await updated; targetVariables = [ {name: 'a', value: '1', supportsDataExplorer: false, type: 'int', size: 54, shape: '', count: 0, truncated: false} @@ -115,8 +122,9 @@ value = 'hello world'`; openVariableExplorer(wrapper); + let updated = waitForVariablesUpdated(); await addCodeImpartial(wrapper, 'a=1\na'); - await waitForUpdate(wrapper, VariableExplorer, 2); + await updated; // Check that we have just the 'a' variable let targetVariables: IJupyterVariable[] = [ @@ -125,8 +133,9 @@ value = 'hello world'`; verifyVariables(wrapper, targetVariables); // Add another variable and check it + updated = waitForVariablesUpdated(); await addCodeImpartial(wrapper, basicCode, 4); - await waitForUpdate(wrapper, VariableExplorer, 3); + await updated; targetVariables = [ {name: 'a', value: '1', supportsDataExplorer: false, type: 'int', size: 54, shape: '', count: 0, truncated: false}, @@ -136,8 +145,9 @@ value = 'hello world'`; verifyVariables(wrapper, targetVariables); // Add a second variable and check it + updated = waitForVariablesUpdated(); await addCodeImpartial(wrapper, basicCode2, 4); - await waitForUpdate(wrapper, VariableExplorer, 4); + await updated; targetVariables = [ {name: 'a', value: '1', supportsDataExplorer: false, type: 'int', size: 54, shape: '', count: 0, truncated: false}, @@ -187,11 +197,11 @@ myDict = {'a': 1}`; openVariableExplorer(wrapper); await addCodeImpartial(wrapper, 'a=1\na'); + const updated = waitForVariablesUpdated(); await addCodeImpartial(wrapper, basicCode, 4); // Verify that we actually update the variable explorer - // Count here is our main render + a render for each variable row as they come in - await waitForUpdate(wrapper, VariableExplorer, 5); + await updated; const targetVariables: IJupyterVariable[] = [ {name: 'a', value: '1', supportsDataExplorer: false, type: 'int', size: 54, shape: '', count: 0, truncated: false}, @@ -219,11 +229,11 @@ myTuple = 1,2,3,4,5,6,7,8,9 openVariableExplorer(wrapper); await addCodeImpartial(wrapper, 'a=1\na'); + const updated = waitForVariablesUpdated(); await addCodeImpartial(wrapper, basicCode, 4); // Verify that we actually update the variable explorer - // Count here is our main render + a render for each variable row as they come in - await waitForUpdate(wrapper, VariableExplorer, 9); + await updated; const targetVariables: IJupyterVariable[] = [ {name: 'a', value: '1', supportsDataExplorer: false, type: 'int', size: 54, shape: '', count: 0, truncated: false}, @@ -255,9 +265,9 @@ strc = 'c'`; openVariableExplorer(wrapper); await addCodeImpartial(wrapper, 'a=1\na'); + const updated = waitForVariablesUpdated(); await addCodeImpartial(wrapper, basicCode, 4); - - await waitForUpdate(wrapper, VariableExplorer, 7); + await updated; let targetVariables: IJupyterVariable[] = [ {name: 'a', value: '1', supportsDataExplorer: false, type: 'int', size: 54, shape: '', count: 0, truncated: false}, @@ -318,6 +328,10 @@ function sortVariableExplorer(wrapper: ReactWrapper, React.Com // Verify a set of rows versus a set of expected variables function verifyVariables(wrapper: ReactWrapper, React.Component>, targetVariables: IJupyterVariable[]) { + // Force an update so we render whatever the current state is + wrapper.update(); + + // Then search for results. const foundRows = wrapper.find('div.react-grid-Row'); expect(foundRows.length).to.be.equal(targetVariables.length, 'Different number of variable explorer rows and target variables'); From a1f28d3c4b1168ae8aaabb86cdd07700f3fc4861 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Wed, 2 Oct 2019 13:53:02 -0700 Subject: [PATCH 2/4] Fix native mime test --- src/test/datascience/nativeEditor.functional.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/datascience/nativeEditor.functional.test.tsx b/src/test/datascience/nativeEditor.functional.test.tsx index a57af32ccc32..b2eb2bb71053 100644 --- a/src/test/datascience/nativeEditor.functional.test.tsx +++ b/src/test/datascience/nativeEditor.functional.test.tsx @@ -127,7 +127,7 @@ for _ in range(50): await addCell(wrapper, matPlotLib, true, 5); verifyHtmlOnCell(wrapper, 'NativeCell', matPlotLibResults, CellPosition.Last); - await addCell(wrapper, spinningCursor, true, 3 + (ioc.mockJupyter ? (cursors.length * 3) : 0)); + await addCell(wrapper, spinningCursor, true, 3 + (ioc.mockJupyter ? (cursors.length * 3) : 50)); verifyHtmlOnCell(wrapper, 'NativeCell', '
', CellPosition.Last); }, () => { return ioc; }); From 680f046bc47fc21eba831217b73ad3c5114172ad Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Wed, 2 Oct 2019 15:06:04 -0700 Subject: [PATCH 3/4] Review feedback --- src/datascience-ui/interactive-common/mainStateController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datascience-ui/interactive-common/mainStateController.ts b/src/datascience-ui/interactive-common/mainStateController.ts index 9f71d2be2c5c..a5d3258e2f83 100644 --- a/src/datascience-ui/interactive-common/mainStateController.ts +++ b/src/datascience-ui/interactive-common/mainStateController.ts @@ -652,7 +652,7 @@ export class MainStateController implements IMessageHandler { } // If the new state includes pendingVariableCount and it's gone to zero, send a message - if ('pendingVariableCount' in newState && this.state.pendingVariableCount === 0 && oldCount !== 0) { + if (this.state.pendingVariableCount === 0 && oldCount !== 0) { setTimeout(() => this.sendMessage(InteractiveWindowMessages.VariablesComplete), 1); } } From 4ea3cbdfa99982b6db1dc93e89576fb085a1bef2 Mon Sep 17 00:00:00 2001 From: Rich Chiodo Date: Wed, 2 Oct 2019 16:56:25 -0700 Subject: [PATCH 4/4] Try to make 2.7 tests pass --- .../datascience/getJupyterVariableValue.py | 6 +-- .../variableexplorer.functional.test.tsx | 44 ++++++------------- 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/pythonFiles/datascience/getJupyterVariableValue.py b/pythonFiles/datascience/getJupyterVariableValue.py index d483eb5cba89..a302cf6df673 100644 --- a/pythonFiles/datascience/getJupyterVariableValue.py +++ b/pythonFiles/datascience/getJupyterVariableValue.py @@ -84,9 +84,9 @@ def __call__(self, obj): return ''.join((x.encode('utf-8') if isinstance(x, unicode) else x) for x in self._repr(obj, 0)) else: return ''.join(self._repr(obj, 0)) - except Exception: + except Exception as e: try: - return 'An exception was raised: %r' % sys.exc_info()[1] + return 'An exception was raised: ' + str(e) except Exception: return 'An exception was raised' @@ -373,7 +373,7 @@ def _bytes_as_unicode_if_possible(self, obj_repr): # locale.getpreferredencoding() and 'utf-8). If no encoding can decode # the input, we return the original bytes. try_encodings = [] - encoding = self.sys_stdout_encoding or getattr(sys.stdout, 'encoding', '') + encoding = self.sys_stdout_encoding or getattr(VC_sys.stdout, 'encoding', '') if encoding: try_encodings.append(encoding.lower()) diff --git a/src/test/datascience/variableexplorer.functional.test.tsx b/src/test/datascience/variableexplorer.functional.test.tsx index 7c58f8c5528d..421a6a7effc8 100644 --- a/src/test/datascience/variableexplorer.functional.test.tsx +++ b/src/test/datascience/variableexplorer.functional.test.tsx @@ -65,10 +65,13 @@ suite('DataScience Interactive Window variable explorer tests', () => { return waitForMessage(ioc, InteractiveWindowMessages.VariablesComplete); } - async function addCodeImpartial(wrapper: ReactWrapper, React.Component>, code: string, expectedRenderCount: number = 4, expectError: boolean = false): Promise, React.Component>> { + async function addCodeImpartial(wrapper: ReactWrapper, React.Component>, code: string, waitForVariables: boolean = true, expectedRenderCount: number = 4, expectError: boolean = false): Promise, React.Component>> { + const variablesUpdated = waitForVariables ? waitForVariablesUpdated() : Promise.resolve(); const nodes = wrapper.find('InteractivePanel'); if (nodes.length > 0) { - return addCode(ioc, wrapper, code, expectedRenderCount, expectError); + const result = await addCode(ioc, wrapper, code, expectedRenderCount, expectError); + await variablesUpdated; + return result; } else { // For the native editor case, we need to create an editor before hand. if (!createdNotebook) { @@ -77,6 +80,7 @@ suite('DataScience Interactive Window variable explorer tests', () => { expectedRenderCount += 1; } await addCell(wrapper, code, true, expectedRenderCount); + await variablesUpdated; return wrapper; } } @@ -90,9 +94,7 @@ value = 'hello world'`; openVariableExplorer(wrapper); await addCodeImpartial(wrapper, 'a=1\na'); - let updated = waitForVariablesUpdated(); - await addCodeImpartial(wrapper, basicCode, 4); - await updated; + await addCodeImpartial(wrapper, basicCode, true, 4); // We should show a string and show an int, the modules should be hidden let targetVariables: IJupyterVariable[] = [ @@ -106,9 +108,7 @@ value = 'hello world'`; ioc.getSettings().datascience.variableExplorerExclude = `${ioc.getSettings().datascience.variableExplorerExclude};str`; // Add another string and check our vars, strings should be hidden - updated = waitForVariablesUpdated(); - await addCodeImpartial(wrapper, basicCode2, 4); - await updated; + await addCodeImpartial(wrapper, basicCode2, true, 4); targetVariables = [ {name: 'a', value: '1', supportsDataExplorer: false, type: 'int', size: 54, shape: '', count: 0, truncated: false} @@ -122,9 +122,7 @@ value = 'hello world'`; openVariableExplorer(wrapper); - let updated = waitForVariablesUpdated(); await addCodeImpartial(wrapper, 'a=1\na'); - await updated; // Check that we have just the 'a' variable let targetVariables: IJupyterVariable[] = [ @@ -133,9 +131,7 @@ value = 'hello world'`; verifyVariables(wrapper, targetVariables); // Add another variable and check it - updated = waitForVariablesUpdated(); - await addCodeImpartial(wrapper, basicCode, 4); - await updated; + await addCodeImpartial(wrapper, basicCode, true, 4); targetVariables = [ {name: 'a', value: '1', supportsDataExplorer: false, type: 'int', size: 54, shape: '', count: 0, truncated: false}, @@ -145,9 +141,7 @@ value = 'hello world'`; verifyVariables(wrapper, targetVariables); // Add a second variable and check it - updated = waitForVariablesUpdated(); - await addCodeImpartial(wrapper, basicCode2, 4); - await updated; + await addCodeImpartial(wrapper, basicCode2, true, 4); targetVariables = [ {name: 'a', value: '1', supportsDataExplorer: false, type: 'int', size: 54, shape: '', count: 0, truncated: false}, @@ -165,7 +159,7 @@ value = 'hello world'`; openVariableExplorer(wrapper); await addCodeImpartial(wrapper, 'a=1\na'); - await addCodeImpartial(wrapper, basicCode, 4); + await addCodeImpartial(wrapper, basicCode, false, 4); // Here we are only going to wait for two renders instead of the needed three // a should have the value updated, but value should still be loading @@ -197,11 +191,7 @@ myDict = {'a': 1}`; openVariableExplorer(wrapper); await addCodeImpartial(wrapper, 'a=1\na'); - const updated = waitForVariablesUpdated(); - await addCodeImpartial(wrapper, basicCode, 4); - - // Verify that we actually update the variable explorer - await updated; + await addCodeImpartial(wrapper, basicCode, true, 4); const targetVariables: IJupyterVariable[] = [ {name: 'a', value: '1', supportsDataExplorer: false, type: 'int', size: 54, shape: '', count: 0, truncated: false}, @@ -229,11 +219,7 @@ myTuple = 1,2,3,4,5,6,7,8,9 openVariableExplorer(wrapper); await addCodeImpartial(wrapper, 'a=1\na'); - const updated = waitForVariablesUpdated(); - await addCodeImpartial(wrapper, basicCode, 4); - - // Verify that we actually update the variable explorer - await updated; + await addCodeImpartial(wrapper, basicCode, true, 4); const targetVariables: IJupyterVariable[] = [ {name: 'a', value: '1', supportsDataExplorer: false, type: 'int', size: 54, shape: '', count: 0, truncated: false}, @@ -265,9 +251,7 @@ strc = 'c'`; openVariableExplorer(wrapper); await addCodeImpartial(wrapper, 'a=1\na'); - const updated = waitForVariablesUpdated(); - await addCodeImpartial(wrapper, basicCode, 4); - await updated; + await addCodeImpartial(wrapper, basicCode, true, 4); let targetVariables: IJupyterVariable[] = [ {name: 'a', value: '1', supportsDataExplorer: false, type: 'int', size: 54, shape: '', count: 0, truncated: false},