@@ -7,7 +7,6 @@ import * as TypeMoq from 'typemoq';
77import * as uuid from 'uuid/v4' ;
88import { CodeLens , Disposable , Position , Range , SourceBreakpoint , Uri } from 'vscode' ;
99import { CancellationToken } from 'vscode-jsonrpc' ;
10- import * as vsls from 'vsls/vscode' ;
1110
1211import { IApplicationShell , IDocumentManager } from '../../client/common/application/types' ;
1312import { RunByLine } from '../../client/common/experimentGroups' ;
@@ -23,11 +22,19 @@ import {
2322 IJupyterDebugService ,
2423 IJupyterExecution
2524} from '../../client/datascience/types' ;
25+ import { ImageButton } from '../../datascience-ui/react-common/imageButton' ;
2626import { DataScienceIocContainer } from './dataScienceIocContainer' ;
2727import { getInteractiveCellResults , getOrCreateInteractiveWindow } from './interactiveWindowTestHelpers' ;
2828import { MockDocument } from './mockDocument' ;
2929import { MockDocumentManager } from './mockDocumentManager' ;
30- import { mountConnectedMainPanel , openVariableExplorer , waitForMessage } from './testHelpers' ;
30+ import { addCell , createNewEditor } from './nativeEditorTestHelpers' ;
31+ import {
32+ getLastOutputCell ,
33+ openVariableExplorer ,
34+ runInteractiveTest ,
35+ runNativeTest ,
36+ waitForMessage
37+ } from './testHelpers' ;
3138import { verifyVariables } from './variableTestHelpers' ;
3239
3340//import { asyncDump } from '../common/asyncDump';
@@ -52,13 +59,45 @@ suite('DataScience Debugger tests', () => {
5259 } ) ;
5360
5461 setup ( async ( ) => {
55- ioc = createContainer ( ) ;
62+ ioc = new DataScienceIocContainer ( ) ;
63+ } ) ;
64+
65+ async function createIOC ( ) {
66+ ioc . registerDataScienceTypes ( ) ;
5667 jupyterDebuggerService = ioc . serviceManager . get < IJupyterDebugService > (
5768 IJupyterDebugService ,
5869 Identifiers . MULTIPLEXING_DEBUGSERVICE
5970 ) ;
60- return ioc . activate ( ) ;
61- } ) ;
71+ // Rebind the appshell so we can change what happens on an error
72+ const dummyDisposable = {
73+ dispose : ( ) => {
74+ return ;
75+ }
76+ } ;
77+ const appShell = TypeMoq . Mock . ofType < IApplicationShell > ( ) ;
78+ appShell . setup ( ( a ) => a . showErrorMessage ( TypeMoq . It . isAnyString ( ) ) ) . returns ( ( e ) => ( lastErrorMessage = e ) ) ;
79+ appShell
80+ . setup ( ( a ) => a . showInformationMessage ( TypeMoq . It . isAny ( ) , TypeMoq . It . isAny ( ) ) )
81+ . returns ( ( ) => Promise . resolve ( '' ) ) ;
82+ appShell
83+ . setup ( ( a ) => a . showInformationMessage ( TypeMoq . It . isAny ( ) , TypeMoq . It . isAny ( ) , TypeMoq . It . isAny ( ) ) )
84+ . returns ( ( _a1 : string , a2 : string , _a3 : string ) => Promise . resolve ( a2 ) ) ;
85+ appShell
86+ . setup ( ( a ) => a . showSaveDialog ( TypeMoq . It . isAny ( ) ) )
87+ . returns ( ( ) => Promise . resolve ( Uri . file ( 'test.ipynb' ) ) ) ;
88+ appShell . setup ( ( a ) => a . setStatusBarMessage ( TypeMoq . It . isAny ( ) ) ) . returns ( ( ) => dummyDisposable ) ;
89+
90+ ioc . serviceManager . rebindInstance < IApplicationShell > ( IApplicationShell , appShell . object ) ;
91+
92+ // Make sure the history provider and execution factory in the container is created (the extension does this on startup in the extension)
93+ // This is necessary to get the appropriate live share services up and running.
94+ ioc . get < IInteractiveWindowProvider > ( IInteractiveWindowProvider ) ;
95+ ioc . get < IJupyterExecution > ( IJupyterExecution ) ;
96+ ioc . get < IDebugLocationTracker > ( IDebugLocationTracker ) ;
97+
98+ await ioc . activate ( ) ;
99+ return ioc ;
100+ }
62101
63102 teardown ( async ( ) => {
64103 for ( const disposable of disposables ) {
@@ -89,42 +128,6 @@ suite('DataScience Debugger tests', () => {
89128 // asyncDump();
90129 } ) ;
91130
92- function createContainer ( ) : DataScienceIocContainer {
93- const result = new DataScienceIocContainer ( ) ;
94- result . registerDataScienceTypes ( ) ;
95-
96- // Rebind the appshell so we can change what happens on an error
97- const dummyDisposable = {
98- dispose : ( ) => {
99- return ;
100- }
101- } ;
102- const appShell = TypeMoq . Mock . ofType < IApplicationShell > ( ) ;
103- appShell . setup ( ( a ) => a . showErrorMessage ( TypeMoq . It . isAnyString ( ) ) ) . returns ( ( e ) => ( lastErrorMessage = e ) ) ;
104- appShell
105- . setup ( ( a ) => a . showInformationMessage ( TypeMoq . It . isAny ( ) , TypeMoq . It . isAny ( ) ) )
106- . returns ( ( ) => Promise . resolve ( '' ) ) ;
107- appShell
108- . setup ( ( a ) => a . showInformationMessage ( TypeMoq . It . isAny ( ) , TypeMoq . It . isAny ( ) , TypeMoq . It . isAny ( ) ) )
109- . returns ( ( _a1 : string , a2 : string , _a3 : string ) => Promise . resolve ( a2 ) ) ;
110- appShell
111- . setup ( ( a ) => a . showSaveDialog ( TypeMoq . It . isAny ( ) ) )
112- . returns ( ( ) => Promise . resolve ( Uri . file ( 'test.ipynb' ) ) ) ;
113- appShell . setup ( ( a ) => a . setStatusBarMessage ( TypeMoq . It . isAny ( ) ) ) . returns ( ( ) => dummyDisposable ) ;
114-
115- result . serviceManager . rebindInstance < IApplicationShell > ( IApplicationShell , appShell . object ) ;
116-
117- // Setup our webview panel
118- result . createWebView ( ( ) => mountConnectedMainPanel ( 'interactive' ) , vsls . Role . None ) ;
119-
120- // Make sure the history provider and execution factory in the container is created (the extension does this on startup in the extension)
121- // This is necessary to get the appropriate live share services up and running.
122- result . get < IInteractiveWindowProvider > ( IInteractiveWindowProvider ) ;
123- result . get < IJupyterExecution > ( IJupyterExecution ) ;
124- result . get < IDebugLocationTracker > ( IDebugLocationTracker ) ;
125- return result ;
126- }
127-
128131 async function debugCell (
129132 code : string ,
130133 breakpoint ?: Range ,
@@ -238,63 +241,108 @@ suite('DataScience Debugger tests', () => {
238241 return [ ] ;
239242 }
240243
241- test ( 'Debug cell without breakpoint' , async ( ) => {
242- await debugCell ( '#%%\nprint("bar")' ) ;
243- } ) ;
244- test ( 'Check variables' , async ( ) => {
245- ioc . setExperimentState ( RunByLine . experiment , true ) ;
246- await debugCell ( '#%%\nx = [4, 6]\nx = 5' , undefined , undefined , false , ( ) => {
247- const targetResult = {
248- name : 'x' ,
249- value : '[4, 6]' ,
250- supportsDataExplorer : true ,
251- type : 'list' ,
252- size : 0 ,
253- shape : '' ,
254- count : 2 ,
255- truncated : false
256- } ;
257- verifyVariables ( ioc ! . wrapper ! , [ targetResult ] ) ;
258- } ) ;
259- } ) ;
260-
261- test ( 'Debug temporary file' , async ( ) => {
262- const code = '#%%\nprint("bar")' ;
263-
264- // Create a dummy document with just this code
265- const docManager = ioc . get < IDocumentManager > ( IDocumentManager ) as MockDocumentManager ;
266- const fileName = 'Untitled-1' ;
267- docManager . addDocument ( code , fileName ) ;
268- const mockDoc = docManager . textDocuments [ 0 ] as MockDocument ;
269- mockDoc . forceUntitled ( ) ;
270-
271- // Start the jupyter server
272- const history = await getOrCreateInteractiveWindow ( ioc ) ;
273- const expectedBreakLine = 2 ; // 2 because of the 'breakpoint()' that gets added
274-
275- // Debug this code. We should either hit the breakpoint or stop on entry
276- const resultPromise = getInteractiveCellResults ( ioc , ioc . wrapper ! , async ( ) => {
277- const breakPromise = createDeferred < void > ( ) ;
278- disposables . push ( jupyterDebuggerService ! . onBreakpointHit ( ( ) => breakPromise . resolve ( ) ) ) ;
279- const targetUri = Uri . file ( fileName ) ;
280- const done = history . debugCode ( code , targetUri . fsPath , 0 , docManager . activeTextEditor ) ;
281- await waitForPromise ( Promise . race ( [ done , breakPromise . promise ] ) , 60000 ) ;
282- assert . ok ( breakPromise . resolved , 'Breakpoint event did not fire' ) ;
283- assert . ok ( ! lastErrorMessage , `Error occurred ${ lastErrorMessage } ` ) ;
284- const stackFrames = await jupyterDebuggerService ! . getStack ( ) ;
285- assert . ok ( stackFrames , 'Stack trace not computable' ) ;
286- assert . ok ( stackFrames . length >= 1 , 'Not enough frames' ) ;
287- assert . equal ( stackFrames [ 0 ] . line , expectedBreakLine , 'Stopped on wrong line number' ) ;
288- assert . ok (
289- stackFrames [ 0 ] . source ! . path ! . includes ( 'baz.py' ) ,
290- 'Stopped on wrong file name. Name should have been saved'
291- ) ;
292- // Verify break location
293- await jupyterDebuggerService ! . continue ( ) ;
294- } ) ;
244+ runInteractiveTest (
245+ 'Debug cell without breakpoint' ,
246+ async ( ) => {
247+ await debugCell ( '#%%\nprint("bar")' ) ;
248+ } ,
249+ createIOC
250+ ) ;
251+ runInteractiveTest (
252+ 'Check variables' ,
253+ async ( ) => {
254+ ioc . setExperimentState ( RunByLine . experiment , true ) ;
255+ await debugCell ( '#%%\nx = [4, 6]\nx = 5' , undefined , undefined , false , ( ) => {
256+ const targetResult = {
257+ name : 'x' ,
258+ value : '[4, 6]' ,
259+ supportsDataExplorer : true ,
260+ type : 'list' ,
261+ size : 0 ,
262+ shape : '' ,
263+ count : 2 ,
264+ truncated : false
265+ } ;
266+ verifyVariables ( ioc ! . wrapper ! , [ targetResult ] ) ;
267+ } ) ;
268+ } ,
269+ createIOC
270+ ) ;
271+
272+ runInteractiveTest (
273+ 'Debug temporary file' ,
274+ async ( ) => {
275+ const code = '#%%\nprint("bar")' ;
276+
277+ // Create a dummy document with just this code
278+ const docManager = ioc . get < IDocumentManager > ( IDocumentManager ) as MockDocumentManager ;
279+ const fileName = 'Untitled-1' ;
280+ docManager . addDocument ( code , fileName ) ;
281+ const mockDoc = docManager . textDocuments [ 0 ] as MockDocument ;
282+ mockDoc . forceUntitled ( ) ;
283+
284+ // Start the jupyter server
285+ const history = await getOrCreateInteractiveWindow ( ioc ) ;
286+ const expectedBreakLine = 2 ; // 2 because of the 'breakpoint()' that gets added
287+
288+ // Debug this code. We should either hit the breakpoint or stop on entry
289+ const resultPromise = getInteractiveCellResults ( ioc , ioc . wrapper ! , async ( ) => {
290+ const breakPromise = createDeferred < void > ( ) ;
291+ disposables . push ( jupyterDebuggerService ! . onBreakpointHit ( ( ) => breakPromise . resolve ( ) ) ) ;
292+ const targetUri = Uri . file ( fileName ) ;
293+ const done = history . debugCode ( code , targetUri . fsPath , 0 , docManager . activeTextEditor ) ;
294+ await waitForPromise ( Promise . race ( [ done , breakPromise . promise ] ) , 60000 ) ;
295+ assert . ok ( breakPromise . resolved , 'Breakpoint event did not fire' ) ;
296+ assert . ok ( ! lastErrorMessage , `Error occurred ${ lastErrorMessage } ` ) ;
297+ const stackFrames = await jupyterDebuggerService ! . getStack ( ) ;
298+ assert . ok ( stackFrames , 'Stack trace not computable' ) ;
299+ assert . ok ( stackFrames . length >= 1 , 'Not enough frames' ) ;
300+ assert . equal ( stackFrames [ 0 ] . line , expectedBreakLine , 'Stopped on wrong line number' ) ;
301+ assert . ok (
302+ stackFrames [ 0 ] . source ! . path ! . includes ( 'baz.py' ) ,
303+ 'Stopped on wrong file name. Name should have been saved'
304+ ) ;
305+ // Verify break location
306+ await jupyterDebuggerService ! . continue ( ) ;
307+ } ) ;
295308
296- const cellResults = await resultPromise ;
297- assert . ok ( cellResults , 'No cell results after finishing debugging' ) ;
298- await history . dispose ( ) ;
299- } ) ;
309+ const cellResults = await resultPromise ;
310+ assert . ok ( cellResults , 'No cell results after finishing debugging' ) ;
311+ await history . dispose ( ) ;
312+ } ,
313+ createIOC
314+ ) ;
315+
316+ runNativeTest (
317+ 'Run by line' ,
318+ async ( ) => {
319+ // Create an editor so something is listening to messages
320+ await createNewEditor ( ioc ) ;
321+ const wrapper = ioc . wrapper ! ;
322+
323+ // Add a cell into the UI and wait for it to render and submit it.
324+ await addCell ( wrapper , ioc , 'a=1\na' , true ) ;
325+
326+ // Step into this cell using the button
327+ let cell = getLastOutputCell ( wrapper , 'NativeCell' ) ;
328+ let ImageButtons = cell . find ( ImageButton ) ;
329+ assert . equal ( ImageButtons . length , 7 , 'Cell buttons not found' ) ;
330+ const runByLineButton = ImageButtons . at ( 3 ) ;
331+ // tslint:disable-next-line: no-any
332+ assert . equal ( ( runByLineButton . instance ( ) . props as any ) . tooltip , 'Run by line' ) ;
333+
334+ const promise = waitForMessage ( ioc , InteractiveWindowMessages . ShowingIp ) ;
335+ runByLineButton . simulate ( 'click' ) ;
336+ await promise ;
337+
338+ // We should be in the break state. See if buttons indicate that or not
339+ cell = getLastOutputCell ( wrapper , 'NativeCell' ) ;
340+ ImageButtons = cell . find ( ImageButton ) ;
341+ assert . equal ( ImageButtons . length , 4 , 'Cell buttons wrong number' ) ;
342+ } ,
343+ ( ) => {
344+ ioc . setExperimentState ( RunByLine . experiment , true ) ;
345+ return createIOC ( ) ;
346+ }
347+ ) ;
300348} ) ;
0 commit comments