@@ -8,10 +8,11 @@ import * as cp from 'child_process';
88import * as fs from 'fs' ;
99import * as http from 'http' ;
1010import { tmpdir } from 'os' ;
11+ import * as net from 'net' ;
1112import * as path from 'path' ;
1213import * as sinon from 'sinon' ;
1314import * as proxy from '../../src/goDebugFactory' ;
14- import { DebugConfiguration } from 'vscode' ;
15+ import { DebugConfiguration , DebugProtocolMessage } from 'vscode' ;
1516import { DebugClient } from 'vscode-debugadapter-testsupport' ;
1617import { ILocation } from 'vscode-debugadapter-testsupport/lib/debugClient' ;
1718import { DebugProtocol } from 'vscode-debugprotocol' ;
@@ -289,12 +290,11 @@ suite('RemoteSourcesAndPackages Tests', () => {
289290
290291// Test suite adapted from:
291292// https://github.com/microsoft/vscode-mock-debug/blob/master/src/tests/adapter.test.ts
292- const testAll = ( isDlvDap : boolean ) => {
293+ const testAll = ( ctx : Mocha . Context , isDlvDap : boolean ) => {
293294 // To disable skipping of dlvDapTests, set dlvDapSkipsEnabled = false.
294295 const dlvDapSkipsEnabled = true ;
295296 const debugConfigProvider = new GoDebugConfigurationProvider ( ) ;
296297 const DEBUG_ADAPTER = path . join ( '.' , 'out' , 'src' , 'debugAdapter' , 'goDebug.js' ) ;
297- let dlvDapProcess : cp . ChildProcess ;
298298
299299 const PROJECT_ROOT = path . normalize ( path . join ( __dirname , '..' , '..' , '..' ) ) ;
300300 const DATA_ROOT = path . join ( PROJECT_ROOT , 'test' , 'testdata' ) ;
@@ -309,18 +309,17 @@ const testAll = (isDlvDap: boolean) => {
309309 } ;
310310
311311 let dc : DebugClient ;
312+ let dlvDapAdapter : DelveDAPDebugAdapterOnSocket ;
312313
313314 setup ( async ( ) => {
314315 if ( isDlvDap ) {
315316 dc = new DebugClient ( 'dlv' , 'dap' , 'go' ) ;
317+ // dc.start will be called in initializeDebugConfig call,
318+ // which creates a thin adapter for delve dap mode,
319+ // runs it on a network port, and gets wired with this dc.
316320
317321 // Launching delve may take longer than the default timeout of 5000.
318322 dc . defaultTimeout = 20_000 ;
319-
320- // Change the output to be printed to the console.
321- sinon . stub ( proxy , 'appendToDebugConsole' ) . callsFake ( ( msg : string ) => {
322- console . log ( msg ) ;
323- } ) ;
324323 return ;
325324 }
326325
@@ -333,9 +332,14 @@ const testAll = (isDlvDap: boolean) => {
333332
334333 teardown ( async ( ) => {
335334 await dc . stop ( ) ;
336- if ( dlvDapProcess ) {
337- await killProcessTree ( dlvDapProcess ) ;
338- dlvDapProcess = null ;
335+ if ( dlvDapAdapter ) {
336+ const d = dlvDapAdapter ;
337+ dlvDapAdapter = null ;
338+ if ( ctx . currentTest ?. state === 'failed' ) {
339+ console . log ( `${ ctx . currentTest ?. title } FAILED: DAP Trace` ) ;
340+ d . printLog ( ) ;
341+ }
342+ await d . dispose ( ) ;
339343 }
340344 sinon . restore ( ) ;
341345 } ) ;
@@ -1812,21 +1816,144 @@ const testAll = (isDlvDap: boolean) => {
18121816
18131817 const debugConfig = await debugConfigProvider . resolveDebugConfiguration ( undefined , config ) ;
18141818 if ( isDlvDap ) {
1815- const { port, dlvDapServer } = await proxy . startDapServer ( debugConfig ) ;
1816- dlvDapProcess = dlvDapServer ;
1817- debugConfig . port = port ; // let the debug test client connect to our dap server.
1818- await dc . start ( port ) ;
1819+ dlvDapAdapter = new DelveDAPDebugAdapterOnSocket ( debugConfig ) ;
1820+ const port = await dlvDapAdapter . serve ( ) ;
1821+ await dc . start ( port ) ; // This will connect to the adapter's port.
18191822 }
18201823 return debugConfig ;
18211824 }
18221825} ;
18231826
18241827suite ( 'Go Debug Adapter Tests (legacy)' , function ( ) {
18251828 this . timeout ( 60_000 ) ;
1826- testAll ( false ) ;
1829+ testAll ( this . ctx , false ) ;
18271830} ) ;
18281831
18291832suite ( 'Go Debug Adapter Tests (dlv-dap)' , function ( ) {
18301833 this . timeout ( 60_000 ) ;
1831- testAll ( true ) ;
1834+ testAll ( this . ctx , true ) ;
18321835} ) ;
1836+
1837+ // DelveDAPDebugAdapterOnSocket runs a DelveDAPOutputAdapter
1838+ // over a network socket. This allows tests to instantiate
1839+ // the thin adapter for Delve DAP and the debug test support's
1840+ // DebugClient to communicate with the adapter over a network socket.
1841+ class DelveDAPDebugAdapterOnSocket extends proxy . DelveDAPOutputAdapter {
1842+ constructor ( config : DebugConfiguration ) {
1843+ super ( config , false ) ;
1844+ }
1845+
1846+ private static TWO_CRLF = '\r\n\r\n' ;
1847+ private _rawData : Buffer ;
1848+ private _contentLength : number ;
1849+ private _writableStream : NodeJS . WritableStream ;
1850+ private _server : net . Server ;
1851+ private _port : number ; // port for the thin adapter.
1852+
1853+ public serve ( ) : Promise < number > {
1854+ return new Promise ( async ( resolve , reject ) => {
1855+ this . _port = await getPort ( ) ;
1856+ this . _server = net . createServer ( ( c ) => {
1857+ this . log ( '>> accepted connection from client' ) ;
1858+ c . on ( 'end' , ( ) => {
1859+ this . log ( '>> client disconnected' ) ;
1860+ this . dispose ( ) ;
1861+ } ) ;
1862+ this . run ( c , c ) ;
1863+ } ) ;
1864+ this . _server . on ( 'error' , ( err ) => reject ( err ) ) ;
1865+ this . _server . listen ( this . _port , ( ) => resolve ( this . _port ) ) ;
1866+ } ) ;
1867+ }
1868+
1869+ private run ( inStream : NodeJS . ReadableStream , outStream : NodeJS . WritableStream ) : void {
1870+ this . _writableStream = outStream ;
1871+ this . _rawData = Buffer . alloc ( 0 ) ;
1872+
1873+ // forward to DelveDAPDebugAdapter, which will forward to dlv dap.
1874+ inStream . on ( 'data' , ( data : Buffer ) => this . _handleData ( data ) ) ;
1875+ // handle data from DelveDAPDebugAdapter, that's from dlv dap.
1876+ this . onDidSendMessage ( ( m ) => this . _send ( m ) ) ;
1877+
1878+ inStream . resume ( ) ;
1879+ }
1880+
1881+ private _disposed = false ;
1882+ public async dispose ( ) {
1883+ if ( this . _disposed ) {
1884+ return ;
1885+ }
1886+ this . _disposed = true ;
1887+ this . log ( 'adapter disposed' ) ;
1888+ await this . _server . close ( ) ;
1889+ await super . dispose ( ) ;
1890+ }
1891+
1892+ // Code from
1893+ // https://github.com/microsoft/vscode-debugadapter-node/blob/2235a2227d1a439372be578cd3f55e15211851b7/testSupport/src/protocolClient.ts#L96-L97
1894+ private _send ( message : DebugProtocolMessage ) : void {
1895+ if ( this . _writableStream ) {
1896+ const json = JSON . stringify ( message ) ;
1897+ this . log ( `<- server: ${ json } ` ) ;
1898+ if ( ! this . _writableStream . writable ) {
1899+ this . log ( 'socket closed already' ) ;
1900+ return ;
1901+ }
1902+ this . _writableStream . write (
1903+ `Content-Length: ${ Buffer . byteLength ( json , 'utf8' ) } ${ DelveDAPDebugAdapterOnSocket . TWO_CRLF } ${ json } ` ,
1904+ 'utf8'
1905+ ) ;
1906+ }
1907+ }
1908+
1909+ // Code from
1910+ // https://github.com/microsoft/vscode-debugadapter-node/blob/2235a2227d1a439372be578cd3f55e15211851b7/testSupport/src/protocolClient.ts#L100-L132
1911+ private _handleData ( data : Buffer ) : void {
1912+ this . _rawData = Buffer . concat ( [ this . _rawData , data ] ) ;
1913+
1914+ // eslint-disable-next-line no-constant-condition
1915+ while ( true ) {
1916+ if ( this . _contentLength >= 0 ) {
1917+ if ( this . _rawData . length >= this . _contentLength ) {
1918+ const message = this . _rawData . toString ( 'utf8' , 0 , this . _contentLength ) ;
1919+ this . _rawData = this . _rawData . slice ( this . _contentLength ) ;
1920+ this . _contentLength = - 1 ;
1921+ if ( message . length > 0 ) {
1922+ try {
1923+ this . log ( `-> server: ${ message } ` ) ;
1924+ const msg : DebugProtocol . ProtocolMessage = JSON . parse ( message ) ;
1925+ this . handleMessage ( msg ) ;
1926+ } catch ( e ) {
1927+ throw new Error ( 'Error handling data: ' + ( e && e . message ) ) ;
1928+ }
1929+ }
1930+ continue ; // there may be more complete messages to process
1931+ }
1932+ } else {
1933+ const idx = this . _rawData . indexOf ( DelveDAPDebugAdapterOnSocket . TWO_CRLF ) ;
1934+ if ( idx !== - 1 ) {
1935+ const header = this . _rawData . toString ( 'utf8' , 0 , idx ) ;
1936+ const lines = header . split ( '\r\n' ) ;
1937+ for ( let i = 0 ; i < lines . length ; i ++ ) {
1938+ const pair = lines [ i ] . split ( / : + / ) ;
1939+ if ( pair [ 0 ] === 'Content-Length' ) {
1940+ this . _contentLength = + pair [ 1 ] ;
1941+ }
1942+ }
1943+ this . _rawData = this . _rawData . slice ( idx + DelveDAPDebugAdapterOnSocket . TWO_CRLF . length ) ;
1944+ continue ;
1945+ }
1946+ }
1947+ break ;
1948+ }
1949+ }
1950+
1951+ /* --- accumulate log messages so we can output when the test fails --- */
1952+ private _log = [ ] as string [ ] ;
1953+ private log ( msg : string ) {
1954+ this . _log . push ( msg ) ;
1955+ }
1956+ public printLog ( ) {
1957+ this . _log . forEach ( ( msg ) => console . log ( msg ) ) ;
1958+ }
1959+ }
0 commit comments