Merge pull request #42 from puremourning/test-framework

Fix and test breakpoint issues
This commit is contained in:
mergify[bot] 2019-07-27 10:28:36 +00:00 • committed by GitHub
commit 28088c991d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 458 additions and 256 deletions

2
.gitignore vendored
View file

@ -13,3 +13,5 @@ package/
README.md.orig.* README.md.orig.*
README.md.toc.* README.md.toc.*
.DS_Store .DS_Store
*.vimspector.log
support/test/csharp/*.exe*

View file

@ -23,8 +23,9 @@ let current_compiler = "vimspector_test"
setlocal errorformat= setlocal errorformat=
\Found\ errors\ in\ %f:%.%#: \Found\ errors\ in\ %f:%.%#:
let &l:makeprg=fnamemodify( findfile( 'run_tests', '.;' ), ':p' ) let s:run_tests = findfile( 'run_tests', '.;' )
\ . ' $* 2>&1' let s:root_dir = fnamemodify( s:run_tests, ':h' )
let &l:makeprg=fnamemodify( s:run_tests, ':p' ) . ' $* 2>&1'
let s:make_cmd = get( g:, 'vimspector_test_make_cmd', 'Make' ) let s:make_cmd = get( g:, 'vimspector_test_make_cmd', 'Make' )
@ -86,17 +87,35 @@ function! s:RunTestUnderCursor()
echo "Running test '" . l:test_func_name . "'" echo "Running test '" . l:test_func_name . "'"
let l:test_arg = expand( '%:p:t' ) . ':' . l:test_func_name let l:test_arg = expand( '%:p:t' ) . ':' . l:test_func_name
execute s:make_cmd . ' ' . l:test_arg let l:cwd = getcwd()
execute 'lcd ' . s:root_dir
try
execute s:make_cmd . ' ' . l:test_arg
finally
execute 'lcd ' . l:cwd
endtry
endfunction endfunction
function! s:RunTest() function! s:RunTest()
update update
execute s:make_cmd . ' %:p:t' let l:cwd = getcwd()
execute 'lcd ' . s:root_dir
try
execute s:make_cmd . ' %:p:t'
finally
execute 'lcd ' . l:cwd
endtry
endfunction endfunction
function! s:RunAllTests() function! s:RunAllTests()
update update
execute s:make_cmd let l:cwd = getcwd()
execute 'lcd ' . s:root_dir
try
execute s:make_cmd
finally
execute 'lcd ' . l:cwd
endtry
endfunction endfunction
if ! has( 'gui_running' ) if ! has( 'gui_running' )

View file

@ -15,16 +15,30 @@
from collections import defaultdict from collections import defaultdict
import abc
import vim import vim
import os import os
import logging
import json import json
from vimspector import utils from vimspector import utils
class ServerBreakpointHandler( object ):
@abc.abstractmethod
def ClearBreakpoints( self ):
pass
@abc.abstractmethod
def AddBreakpoints( self, source, message ):
pass
class ProjectBreakpoints( object ): class ProjectBreakpoints( object ):
def __init__( self ): def __init__( self ):
self._connection = None self._connection = None
self._logger = logging.getLogger( __name__ )
utils.SetUpLogging( self._logger )
# These are the user-entered breakpoints. # These are the user-entered breakpoints.
self._line_breakpoints = defaultdict( list ) self._line_breakpoints = defaultdict( list )
@ -55,6 +69,7 @@ class ProjectBreakpoints( object ):
self._exceptionBreakpoints = None self._exceptionBreakpoints = None
self._server_capabilities = {} self._server_capabilities = {}
self._connection = None self._connection = None
self.UpdateUI()
# for each breakpoint: # for each breakpoint:
# clear its resolved status # clear its resolved status
@ -94,6 +109,12 @@ class ProjectBreakpoints( object ):
def ClearBreakpoints( self ): def ClearBreakpoints( self ):
# These are the user-entered breakpoints. # These are the user-entered breakpoints.
for file_name, breakpoints in self._line_breakpoints.items():
for bp in breakpoints:
if 'sign_id' in bp:
vim.command( 'sign unplace {0} group=VimspectorBP'.format(
bp[ 'sign_id' ] ) )
self._line_breakpoints = defaultdict( list ) self._line_breakpoints = defaultdict( list )
self._func_breakpoints = [] self._func_breakpoints = []
self._exceptionBreakpoints = None self._exceptionBreakpoints = None
@ -108,16 +129,26 @@ class ProjectBreakpoints( object ):
return return
found_bp = False found_bp = False
action = 'New'
for index, bp in enumerate( self._line_breakpoints[ file_name ] ): for index, bp in enumerate( self._line_breakpoints[ file_name ] ):
if bp[ 'line' ] == line: if bp[ 'line' ] == line:
found_bp = True found_bp = True
if bp[ 'state' ] == 'ENABLED': if bp[ 'state' ] == 'ENABLED' and not self._connection:
bp[ 'state' ] = 'DISABLED' bp[ 'state' ] = 'DISABLED'
action = 'Disable'
else: else:
if 'sign_id' in bp: if 'sign_id' in bp:
vim.command( 'sign unplace {0} group=VimspectorBP'.format( vim.command( 'sign unplace {0} group=VimspectorBP'.format(
bp[ 'sign_id' ] ) ) bp[ 'sign_id' ] ) )
del self._line_breakpoints[ file_name ][ index ] del self._line_breakpoints[ file_name ][ index ]
action = 'Delete'
break
self._logger.debug( "Toggle found bp at {}:{} ? {} ({})".format(
file_name,
line,
found_bp,
action ) )
if not found_bp: if not found_bp:
self._line_breakpoints[ file_name ].append( { self._line_breakpoints[ file_name ].append( {
@ -157,23 +188,22 @@ class ProjectBreakpoints( object ):
def SendBreakpoints( self ): def SendBreakpoints( self ):
if not self._breakpoints_handler: assert self._breakpoints_handler is not None
def handler( source, msg ):
return self._ShowBreakpoints() # Clear any existing breakpoints prior to sending new ones
else: self._breakpoints_handler.ClearBreakpoints()
handler = self._breakpoints_handler
for file_name, line_breakpoints in self._line_breakpoints.items(): for file_name, line_breakpoints in self._line_breakpoints.items():
breakpoints = [] breakpoints = []
for bp in line_breakpoints: for bp in line_breakpoints:
if bp[ 'state' ] != 'ENABLED':
continue
if 'sign_id' in bp: if 'sign_id' in bp:
vim.command( 'sign unplace {0} group=VimspectorBP'.format( vim.command( 'sign unplace {0} group=VimspectorBP'.format(
bp[ 'sign_id' ] ) ) bp[ 'sign_id' ] ) )
del bp[ 'sign_id' ] del bp[ 'sign_id' ]
if bp[ 'state' ] != 'ENABLED':
continue
breakpoints.append( { 'line': bp[ 'line' ] } ) breakpoints.append( { 'line': bp[ 'line' ] } )
source = { source = {
@ -182,7 +212,7 @@ class ProjectBreakpoints( object ):
} }
self._connection.DoRequest( self._connection.DoRequest(
lambda msg: handler( source, msg ), lambda msg: self._breakpoints_handler.AddBreakpoints( source, msg ),
{ {
'command': 'setBreakpoints', 'command': 'setBreakpoints',
'arguments': { 'arguments': {
@ -195,7 +225,7 @@ class ProjectBreakpoints( object ):
if self._server_capabilities.get( 'supportsFunctionBreakpoints' ): if self._server_capabilities.get( 'supportsFunctionBreakpoints' ):
self._connection.DoRequest( self._connection.DoRequest(
lambda msg: handler( None, msg ), lambda msg: self._breakpoints_handler.AddBreakpoints( None, msg ),
{ {
'command': 'setFunctionBreakpoints', 'command': 'setFunctionBreakpoints',
'arguments': { 'arguments': {

View file

@ -128,23 +128,11 @@ class CodeView( object ):
# Not found. Assume new # Not found. Assume new
self.AddBreakpoints( None, [ bp ] ) self.AddBreakpoints( None, [ bp ] )
def DeleteBreakpoint( self, bp ):
if 'id' not in bp:
return
for _, breakpoint_list in self._breakpoints.items():
for index, breakpoint in enumerate( breakpoint_list ):
if 'id' in breakpoint and breakpoint[ 'id' ] == bp[ 'id' ]:
del breakpoint_list[ index ]
return
# Not found. Shrug.
def _UndisplaySigns( self ): def _UndisplaySigns( self ):
for sign_id in self._signs[ 'breakpoints' ]: for sign_id in self._signs[ 'breakpoints' ]:
vim.command( 'sign unplace {} group=VimspectorCode'.format( sign_id ) ) vim.command( 'sign unplace {} group=VimspectorCode'.format( sign_id ) )
self._signs[ 'breakpoints' ].clear() self._signs[ 'breakpoints' ] = []
def ClearBreakpoints( self ): def ClearBreakpoints( self ):
self._UndisplaySigns() self._UndisplaySigns()
@ -169,7 +157,7 @@ class CodeView( object ):
sign_id, sign_id,
breakpoint[ 'line' ], breakpoint[ 'line' ],
'vimspectorBP' if breakpoint[ 'verified' ] 'vimspectorBP' if breakpoint[ 'verified' ]
else 'vimspectorBPDisabled', else 'vimspectorBPDisabled',
file_name ) ) file_name ) )

View file

@ -19,6 +19,7 @@ import json
import os import os
import subprocess import subprocess
import shlex import shlex
import traceback
from vimspector import ( breakpoints, from vimspector import ( breakpoints,
code, code,
@ -39,6 +40,7 @@ class DebugSession( object ):
self._logger = logging.getLogger( __name__ ) self._logger = logging.getLogger( __name__ )
utils.SetUpLogging( self._logger ) utils.SetUpLogging( self._logger )
self._logger.info( "**** INITIALISING NEW VIMSPECTOR SESSION ****" )
self._logger.info( 'VIMSPECTOR_HOME = %s', VIMSPECTOR_HOME ) self._logger.info( 'VIMSPECTOR_HOME = %s', VIMSPECTOR_HOME )
self._logger.info( 'gadgetDir = %s', self._logger.info( 'gadgetDir = %s',
install.GetGadgetDir( VIMSPECTOR_HOME, install.GetGadgetDir( VIMSPECTOR_HOME,
@ -63,6 +65,8 @@ class DebugSession( object ):
self._server_capabilities = {} self._server_capabilities = {}
def Start( self, launch_variables = {} ): def Start( self, launch_variables = {} ):
self._logger.info( "User requested start debug session with %s",
launch_variables )
self._configuration = None self._configuration = None
self._adapter = None self._adapter = None
@ -162,13 +166,16 @@ class DebugSession( object ):
def _StartWithConfiguration( self, configuration, adapter ): def _StartWithConfiguration( self, configuration, adapter ):
def start(): def start():
self._logger.debug( "Starting debugger from stack context: %s",
traceback.format_stack() )
self._configuration = configuration self._configuration = configuration
self._adapter = adapter self._adapter = adapter
self._logger.info( 'Configuration: {0}'.format( json.dumps( self._logger.info( 'Configuration: %s',
self._configuration ) ) ) json.dumps( self._configuration ) )
self._logger.info( 'Adapter: {0}'.format( json.dumps( self._logger.info( 'Adapter: %s',
self._adapter ) ) ) json.dumps( self._adapter ) )
if not self._uiTab: if not self._uiTab:
self._SetUpUI() self._SetUpUI()
@ -183,16 +190,23 @@ class DebugSession( object ):
self._outputView.ConnectionUp( self._connection ) self._outputView.ConnectionUp( self._connection )
self._breakpoints.ConnectionUp( self._connection ) self._breakpoints.ConnectionUp( self._connection )
def update_breakpoints( source, message ): class Handler( breakpoints.ServerBreakpointHandler ):
if 'body' not in message: def __init__( self, codeView ):
return self.codeView = codeView
self._codeView.AddBreakpoints( source,
message[ 'body' ][ 'breakpoints' ] )
self._codeView.ShowBreakpoints()
self._breakpoints.SetBreakpointsHandler( update_breakpoints ) def ClearBreakpoints( self ):
self.codeView.ClearBreakpoints()
def AddBreakpoints( self, source, message ):
if 'body' not in message:
return
self.codeView.AddBreakpoints( source,
message[ 'body' ][ 'breakpoints' ] )
self._breakpoints.SetBreakpointsHandler( Handler( self._codeView ) )
if self._connection: if self._connection:
self._logger.debug( "_StopDebugAdapter with callback: start" )
self._StopDebugAdapter( start ) self._StopDebugAdapter( start )
return return
@ -212,6 +226,7 @@ class DebugSession( object ):
if self._connection: if self._connection:
self._connection.OnData( data ) self._connection.OnData( data )
def OnServerStderr( self, data ): def OnServerStderr( self, data ):
self._logger.info( "Server stderr: %s", data ) self._logger.info( "Server stderr: %s", data )
if self._outputView: if self._outputView:
@ -227,22 +242,31 @@ class DebugSession( object ):
self._connection = None self._connection = None
def Stop( self ): def Stop( self ):
self._logger.debug( "Stop debug adapter with no callback" )
self._StopDebugAdapter() self._StopDebugAdapter()
def Reset( self ): def Reset( self ):
if self._connection: if self._connection:
self._logger.debug( "Stop debug adapter with callback : self._Reset()" )
self._StopDebugAdapter( lambda: self._Reset() ) self._StopDebugAdapter( lambda: self._Reset() )
else: else:
self._Reset() self._Reset()
def _Reset( self ): def _Reset( self ):
self._logger.info( "Debugging complete." )
if self._uiTab: if self._uiTab:
self._logger.debug( "Clearing down UI with stack_trace: %s",
traceback.format_stack() )
vim.current.tabpage = self._uiTab vim.current.tabpage = self._uiTab
self._stackTraceView.Reset() self._stackTraceView.Reset()
self._variablesView.Reset() self._variablesView.Reset()
self._outputView.Reset() self._outputView.Reset()
self._codeView.Reset() self._codeView.Reset()
vim.command( 'tabclose!' ) vim.command( 'tabclose!' )
self._stackTraceView = None
self._variablesView = None
self._outputView = None
self._codeView = None
self._uiTab = None self._uiTab = None
# make sure that we're displaying signs in any still-open buffers # make sure that we're displaying signs in any still-open buffers
@ -312,9 +336,9 @@ class DebugSession( object ):
self._variablesView.ShowBalloon( self._stackTraceView.GetCurrentFrame(), self._variablesView.ShowBalloon( self._stackTraceView.GetCurrentFrame(),
expression ) expression )
else: else:
self._logger.debug( 'Winnr {0} is not the code window {1}'.format( self._logger.debug( 'Winnr %s is not the code window %s',
winnr, winnr,
self._codeView._window.number ) ) self._codeView._window.number )
def ExpandFrameOrThread( self ): def ExpandFrameOrThread( self ):
self._stackTraceView.ExpandFrameOrThread() self._stackTraceView.ExpandFrameOrThread()
@ -387,8 +411,8 @@ class DebugSession( object ):
persist = True ) persist = True )
return return
self._logger.info( 'Starting debug adapter with: {0}'.format( json.dumps( self._logger.info( 'Starting debug adapter with: %s',
self._adapter ) ) ) json.dumps( self._adapter ) )
self._init_complete = False self._init_complete = False
self._on_init_complete_handlers = [] self._on_init_complete_handlers = []
@ -428,13 +452,14 @@ class DebugSession( object ):
def _StopDebugAdapter( self, callback = None ): def _StopDebugAdapter( self, callback = None ):
def handler( *args ): def handler( *args ):
vim.eval( 'vimspector#internal#{}#StopDebugSession()'.format(
self._connection_type ) )
if callback: if callback:
self._logger.debug( "Setting server exit handler before disconnect" )
assert not self._run_on_server_exit assert not self._run_on_server_exit
self._run_on_server_exit = callback self._run_on_server_exit = callback
vim.eval( 'vimspector#internal#{}#StopDebugSession()'.format(
self._connection_type ) )
arguments = {} arguments = {}
if self._server_capabilities.get( 'supportTerminateDebuggee' ): if self._server_capabilities.get( 'supportTerminateDebuggee' ):
arguments[ 'terminateDebugee' ] = True arguments[ 'terminateDebugee' ] = True
@ -638,6 +663,11 @@ class DebugSession( object ):
# doesn't respond top threads request when attaching via gdbserver. At # doesn't respond top threads request when attaching via gdbserver. At
# least it would apear that way. # least it would apear that way.
# #
# As it turns out this is due to a bug in gdbserver which means that
# attachment doesn't work due to sending the signal to the process group
# leader rather than the process. The workaround is to manually SIGTRAP the
# PID.
#
if self._launch_complete and self._init_complete: if self._launch_complete and self._init_complete:
for h in self._on_init_complete_handlers: for h in self._on_init_complete_handlers:
h() h()
@ -719,6 +749,8 @@ class DebugSession( object ):
self._variablesView.Clear() self._variablesView.Clear()
def OnServerExit( self, status ): def OnServerExit( self, status ):
self._logger.info( "The server has terminated with status %s",
status )
self.Clear() self.Clear()
self._connection.Reset() self._connection.Reset()
@ -730,11 +762,14 @@ class DebugSession( object ):
self._ResetServerState() self._ResetServerState()
if self._run_on_server_exit: if self._run_on_server_exit:
self._logger.debug( "Running server exit handler" )
self._run_on_server_exit() self._run_on_server_exit()
else:
self._logger.debug( "No server exit handler" )
def OnEvent_terminated( self, message ): def OnEvent_terminated( self, message ):
# We will handle this when the server actually exists # We will handle this when the server actually exists
utils.UserMessage( "Debugging was terminated." ) utils.UserMessage( "Debugging was terminated by the server." )
def OnEvent_output( self, message ): def OnEvent_output( self, message ):
if self._outputView: if self._outputView:
@ -771,6 +806,9 @@ class DebugSession( object ):
return self._breakpoints.ToggleBreakpoint() return self._breakpoints.ToggleBreakpoint()
def ClearBreakpoints( self ): def ClearBreakpoints( self ):
if self._connection:
self._codeView.ClearBreakpoints()
return self._breakpoints.ClearBreakpoints() return self._breakpoints.ClearBreakpoints()
def AddFunctionBreakpoint( self, function ): def AddFunctionBreakpoint( self, function ):

View file

@ -103,7 +103,7 @@ class OutputView( object ):
if 'E516' not in e: if 'E516' not in e:
raise raise
self._buffers.clear() self._buffers = {}
def _ShowOutput( self, category ): def _ShowOutput( self, category ):
utils.JumpToWindow( self._window ) utils.JumpToWindow( self._window )

View file

@ -69,8 +69,6 @@ def SetUpCommandBuffer( cmd, name ):
name, name,
cmd ) ) cmd ) )
UserMessage( 'Bufs: {}'.format( [ int( b ) for b in bufs ] ), persist = True )
return [ vim.buffers[ b ] for b in bufs ] return [ vim.buffers[ b ] for b in bufs ]
@ -227,6 +225,11 @@ def Escape( msg ):
def UserMessage( msg, persist=False ): def UserMessage( msg, persist=False ):
if persist:
_logger.warning( 'User Msg: ' + msg )
else:
_logger.info( 'User Msg: ' + msg )
vim.command( 'redraw' ) vim.command( 'redraw' )
cmd = 'echom' if persist else 'echo' cmd = 'echom' if persist else 'echo'
for line in msg.split( '\n' ): for line in msg.split( '\n' ):
@ -283,7 +286,7 @@ def AppendToBuffer( buf, line_or_lines, modified=False ):
except Exception: except Exception:
# There seem to be a lot of Vim bugs that lead to E315, whose help says that # There seem to be a lot of Vim bugs that lead to E315, whose help says that
# this is an internal error. Ignore the error, but write a trace to the log. # this is an internal error. Ignore the error, but write a trace to the log.
logging.getLogger( __name__ ).exception( _logger.exception(
'Internal error while updating buffer %s (%s)', buf.name, buf.number ) 'Internal error while updating buffer %s (%s)', buf.name, buf.number )
finally: finally:
if not modified: if not modified:

View file

@ -1,20 +1,9 @@
source lib/shared.vim
function! SetUp() function! SetUp()
if exists ( 'g:loaded_vimpector' ) call vimspector#test#setup#SetUpWithMappings( v:none )
unlet g:loaded_vimpector
endif
source vimrc
" This is a bit of a hack
runtime! plugin/**/*.vim
endfunction endfunction
function! ClearDown() function! ClearDown()
if exists( '*vimspector#internal#state#Reset' ) call vimspector#test#setup#ClearDown()
call vimspector#internal#state#Reset()
endif
endfunction endfunction
function! SetUp_Test_Mappings_Are_Added_HUMAN() function! SetUp_Test_Mappings_Are_Added_HUMAN()
@ -62,40 +51,27 @@ function! Test_Signs_Placed_Using_API_Are_Shown()
call vimspector#ToggleBreakpoint() call vimspector#ToggleBreakpoint()
call assert_true( exists( '*vimspector#ToggleBreakpoint' ) ) call assert_true( exists( '*vimspector#ToggleBreakpoint' ) )
call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP',
let signs = sign_getplaced( '%', { \ line( '.' ),
\ 'group': 'VimspectorBP', \ 'vimspectorBP' )
\ 'line': line( '.' )
\ } )
call assert_equal( 1, len( signs ) )
call assert_equal( 1, len( signs[ 0 ].signs ) )
call assert_equal( 'vimspectorBP', signs[ 0 ].signs[ 0 ].name )
" Disable breakpoint " Disable breakpoint
call vimspector#ToggleBreakpoint() call vimspector#ToggleBreakpoint()
call vimspector#test#signs#AssertSignGroupSingletonAtLine(
let signs = sign_getplaced( '%', { \ 'VimspectorBP',
\ 'group': 'VimspectorBP', \ line( '.' ),
\ 'line': line( '.' ) \ 'vimspectorBPDisabled' )
\ } )
call assert_equal( 1, len( signs ) )
call assert_equal( 1, len( signs[ 0 ].signs ) )
call assert_equal( 'vimspectorBPDisabled', signs[ 0 ].signs[ 0 ].name )
" Remove breakpoint " Remove breakpoint
call vimspector#ToggleBreakpoint() call vimspector#ToggleBreakpoint()
let signs = sign_getplaced( '%', { call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP',
\ 'group': 'VimspectorBP', \ line( '.' ) )
\ 'line': line( '.' )
\ } )
call assert_equal( 1, len( signs ) )
call assert_equal( 0, len( signs[ 0 ].signs ) )
call vimspector#ClearBreakpoints() call vimspector#ClearBreakpoints()
call vimspector#test#signs#AssertSignGroupEmpty( 'VimspectorBP' )
call vimspector#test#signs#AssertSignGroupEmpty( 'VimspectorCode' )
%bwipeout! %bwipeout!
endfunction endfunction
@ -106,54 +82,34 @@ endfunction
function! Test_Use_Mappings_HUMAN() function! Test_Use_Mappings_HUMAN()
lcd testdata/cpp/simple lcd testdata/cpp/simple
edit simple.cpp edit simple.cpp
call setpos( '.', [ 0, 15, 1 ] )
15 call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 )
call assert_equal( 15, line( '.' ) ) call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 15 )
" Add the breakpoing " Add the breakpoint
call feedkeys( "\<F9>", 'xt' ) call feedkeys( "\<F9>", 'xt' )
call vimspector#test#signs#AssertSignGroupSingletonAtLine( 'VimspectorBP',
let signs = sign_getplaced( '%', { \ 15,
\ 'group': 'VimspectorBP', \ 'vimspectorBP' )
\ 'line': line( '.' )
\ } )
call assert_equal( 1, len( signs ), 1 )
call assert_equal( 1, len( signs[ 0 ].signs ), 1 )
call assert_equal( 'vimspectorBP', signs[ 0 ].signs[ 0 ].name )
" Disable the breakpoint " Disable the breakpoint
call feedkeys( "\<F9>", 'xt' ) call feedkeys( "\<F9>", 'xt' )
call vimspector#test#signs#AssertSignGroupSingletonAtLine(
let signs = sign_getplaced( '%', { \ 'VimspectorBP',
\ 'group': 'VimspectorBP', \ 15,
\ 'line': line( '.' ) \ 'vimspectorBPDisabled' )
\ } )
call assert_equal( 1, len( signs ), 1 )
call assert_equal( 1, len( signs[ 0 ].signs ), 1 )
call assert_equal( 'vimspectorBPDisabled', signs[ 0 ].signs[ 0 ].name )
" Delete the breakpoint " Delete the breakpoint
call feedkeys( "\<F9>", 'xt' ) call feedkeys( "\<F9>", 'xt' )
call vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorBP', 15 )
let signs = sign_getplaced( '%', {
\ 'group': 'VimspectorBP',
\ 'line': line( '.' )
\ } )
call assert_equal( 1, len( signs ), 1 )
call assert_equal( 0, len( signs[ 0 ].signs ) )
" Add it again " Add it again
call feedkeys( "\<F9>", 'xt' ) call feedkeys( "\<F9>", 'xt' )
call vimspector#test#signs#AssertSignGroupSingletonAtLine(
let signs = sign_getplaced( '%', { \ 'VimspectorBP',
\ 'group': 'VimspectorBP', \ 15,
\ 'line': line( '.' ) \ 'vimspectorBP' )
\ } )
call assert_equal( 1, len( signs ), 1 )
call assert_equal( 1, len( signs[ 0 ].signs ), 1 )
call assert_equal( 'vimspectorBP', signs[ 0 ].signs[ 0 ].name )
" Here we go. Start Debugging " Here we go. Start Debugging
call feedkeys( "\<F5>", 'xt' ) call feedkeys( "\<F5>", 'xt' )
@ -162,72 +118,130 @@ function! Test_Use_Mappings_HUMAN()
let cur_tabnr = tabpagenr() let cur_tabnr = tabpagenr()
call assert_equal( 5, len( gettabinfo( cur_tabnr )[ 0 ].windows ) ) call assert_equal( 5, len( gettabinfo( cur_tabnr )[ 0 ].windows ) )
call WaitForAssert( {-> call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 )
\ assert_equal( 'simple.cpp', bufname( '%' ), 'Current buffer' )
\ }, 10000 )
call assert_equal( 15, line( '.' ), 'Current line' )
call assert_equal( 1, col( '.' ), 'Current column' )
let signs = sign_getplaced( '%', {
\ 'group': 'VimspectorCode',
\ } )
call assert_equal( 1, len( signs ), 'Sign-buffers' )
call assert_equal( 2, len( signs[ 0 ].signs ), 'Signs in buffer' )
let pc_index = -1
let index = 0
while index < len( signs[ 0 ].signs )
let s = signs[ 0 ].signs[ index ]
if s.name ==# 'vimspectorPC'
if pc_index >= 0
call assert_report( 'Found too many PC signs!' )
endif
let pc_index = index
endif
let index = index + 1
endwhile
call assert_true( pc_index >= 0 )
call assert_equal( 15, signs[ 0 ].signs[ pc_index ].lnum )
" Step " Step
call feedkeys( "\<F10>", 'xt' ) call feedkeys( "\<F10>", 'xt' )
call WaitForAssert( {-> assert_equal( 16, line( '.' ), 'Current line' ) } ) call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 16, 1 )
call assert_equal( 'simple.cpp', bufname( '%' ), 'Current buffer' ) call WaitForAssert( {->
call assert_equal( 1, col( '.' ), 'Current column' ) \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cp', 16 )
\ } )
let signs = sign_getplaced( '%', { call vimspector#test#setup#Reset()
\ 'group': 'VimspectorCode',
\ } ) lcd -
%bwipeout!
call assert_equal( 1, len( signs ), 'Sign-buffers' ) endfunction
call assert_equal( 2, len( signs[ 0 ].signs ), 'Signs in buffer' )
function! SetUp_Test_StopAtEntry()
let pc_index = -1 let g:vimspector_enable_mappings = 'HUMAN'
let index = 0 endfunction
while index < len( signs[ 0 ].signs )
let s = signs[ 0 ].signs[ index ] function Test_StopAtEntry()
if s.name ==# 'vimspectorPC' lcd testdata/cpp/simple
if pc_index >= 0 edit simple.cpp
call assert_report( 'Found too many PC signs!' ) call setpos( '.', [ 0, 1, 1 ] )
endif
let pc_index = index " Test stopAtEntry behaviour
endif call feedkeys( "\<F5>", 'xt' )
let index = index + 1
endwhile call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 )
call assert_true( pc_index >= 0 ) call WaitForAssert( {->
call assert_equal( 16, signs[ 0 ].signs[ pc_index ].lnum ) \ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 15 )
\ } )
call vimspector#Reset()
call vimspector#test#setup#Reset()
call vimspector#ClearBreakpoints()
lcd -
let signs = sign_getplaced( '%', { %bwipeout!
\ 'group': 'VimspectorBP' endfunction
\ } )
call assert_equal( 1, len( signs ), 1 ) function! SetUp_Test_DisableBreakpointWhileDebugging()
call assert_equal( 0, len( signs[ 0 ].signs ) ) let g:vimspector_enable_mappings = 'HUMAN'
endfunction
function Test_DisableBreakpointWhileDebugging()
lcd testdata/cpp/simple
edit simple.cpp
call setpos( '.', [ 0, 15, 1 ] )
" Test stopAtEntry behaviour
call feedkeys( "\<F5>", 'xt' )
call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 15, 1 )
call WaitForAssert( {->
\ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 15 )
\ } )
call vimspector#test#signs#AssertSignGroupEmpty( 'VimspectorBP' )
call setpos( '.', [ 0, 16, 1 ] )
" Add the breakpoint
call feedkeys( "\<F9>", 'xt' )
call WaitForAssert( {->
\ vimspector#test#signs#AssertSignGroupSingletonAtLine(
\ 'VimspectorCode',
\ 16,
\ 'vimspectorBP' )
\ } )
" Remove the breakpoint
call feedkeys( "\<F9>", 'xt' )
call WaitForAssert( {->
\ vimspector#test#signs#AssertSignGroupEmptyAtLine( 'VimspectorCode',
\ 16 )
\ } )
" Add the breakpoint
call feedkeys( "\<F9>", 'xt' )
call WaitForAssert( {->
\ vimspector#test#signs#AssertSignGroupSingletonAtLine(
\ 'VimspectorCode',
\ 16,
\ 'vimspectorBP' )
\ } )
" Run to breakpoint
call setpos( '.', [ 0, 15, 1 ] )
call feedkeys( "\<F5>", 'xt' )
call vimspector#test#signs#AssertCursorIsAtLineInBuffer( 'simple.cpp', 16, 1 )
call WaitForAssert( {->
\ vimspector#test#signs#AssertPCIsAtLineInBuffer( 'simple.cpp', 16 )
\ } )
call vimspector#Reset()
call WaitForAssert( {->
\ assert_true ( pyxeval( '_vimspector_session._connection is None' ) )
\ } )
call WaitForAssert( {->
\ assert_true( pyxeval( '_vimspector_session._uiTab is None' ) )
\ } )
" Check breakpoint is now a user breakpoint
call setpos( '.', [ bufnr( 'simple.cpp' ), 1, 1 ] )
call vimspector#test#signs#AssertSignGroupSingletonAtLine(
\ 'VimspectorBP',
\ 16,
\ 'vimspectorBP' )
" Disable the breakpoint
call setpos( '.', [ bufnr( 'simple.cpp' ), 16, 1 ] )
call feedkeys( "\<F9>", 'xt' )
call vimspector#test#signs#AssertSignGroupSingletonAtLine(
\ 'VimspectorBP',
\ 16,
\ 'vimspectorBPDisabled' )
" And delete it
call feedkeys( "\<F9>", 'xt' )
call vimspector#test#signs#AssertSignGroupEmptyAtLine(
\ 'VimspectorBP',
\ 16 )
call vimspector#ClearBreakpoints()
call vimspector#test#signs#AssertSignGroupEmpty( 'VimspectorBP' )
call vimspector#test#signs#AssertSignGroupEmpty( 'VimspectorCode' )
lcd - lcd -
%bwipeout! %bwipeout!

View file

@ -0,0 +1,36 @@
function! vimspector#test#setup#SetUpWithMappings( mappings ) abort
if exists ( 'g:loaded_vimpector' )
unlet g:loaded_vimpector
endif
if a:mappings != v:none
let g:vimspector_enable_mappings = a:mappings
endif
source vimrc
" This is a bit of a hack
runtime! plugin/**/*.vim
endfunction
function! vimspector#test#setup#ClearDown() abort
endfunction
function! vimspector#test#setup#Reset() abort
call vimspector#Reset()
call WaitForAssert( {->
\ assert_true( pyxeval( '_vimspector_session._connection is None' ) )
\ } )
call WaitForAssert( {->
\ assert_true( pyxeval( '_vimspector_session._uiTab is None' ) )
\ }, 10000 )
call vimspector#test#signs#AssertSignGroupEmpty( 'VimspectorCode' )
call vimspector#ClearBreakpoints()
call vimspector#test#signs#AssertSignGroupEmpty( 'VimspectorBP' )
if exists( '*vimspector#internal#state#Reset' )
call vimspector#internal#state#Reset()
endif
endfunction

View file

@ -0,0 +1,93 @@
function! vimspector#test#signs#AssertCursorIsAtLineInBuffer( buffer,
\ line,
\ column ) abort
call WaitForAssert( {->
\ assert_equal( a:buffer, bufname( '%' ), 'Current buffer' )
\ }, 10000 )
call WaitForAssert( {->
\ assert_equal( a:line, line( '.' ), 'Current line' )
\ }, 10000 )
call assert_equal( a:column, col( '.' ), 'Current column' )
endfunction
function! vimspector#test#signs#AssertPCIsAtLineInBuffer( buffer, line ) abort
let signs = sign_getplaced( a:buffer, {
\ 'group': 'VimspectorCode',
\ } )
if assert_equal( 1, len( signs ), 'Number of buffers named ' . a:buffer )
return 1
endif
if assert_true( len( signs[ 0 ].signs ) >= 1,
\ 'At least one VimspectorCode sign' )
return 1
endif
let pc_index = -1
let index = 0
while index < len( signs[ 0 ].signs )
let s = signs[ 0 ].signs[ index ]
if s.name ==# 'vimspectorPC'
if assert_false( pc_index >= 0, 'Too many PC signs' )
return 1
endif
let pc_index = index
endif
let index = index + 1
endwhile
return assert_true( pc_index >= 0 ) ||
\ assert_equal( a:line, signs[ 0 ].signs[ pc_index ].lnum )
endfunction
function! vimspector#test#signs#AssertSignGroupSingletonAtLine( group,
\ line,
\ sign_name )
\ abort
let signs = sign_getplaced( '%', {
\ 'group': a:group,
\ 'lnum': a:line,
\ } )
return assert_equal( 1,
\ len( signs ),
\ 'Num buffers named %' ) ||
\ assert_equal( 1,
\ len( signs[ 0 ].signs ),
\ 'Num signs in ' . a:group . ' at ' . a:line ) ||
\ assert_equal( a:sign_name,
\ signs[ 0 ].signs[ 0 ].name,
\ 'Sign in group ' . a:group . ' at ' . a:line )
endfunction
function! vimspector#test#signs#AssertSignGroupEmptyAtLine( group, line ) abort
let signs = sign_getplaced( '%', {
\ 'group': a:group,
\ 'lnum': line( '.' )
\ } )
return assert_equal( 1,
\ len( signs ),
\ 'Num buffers named %' ) ||
\ assert_equal( 0,
\ len( signs[ 0 ].signs ),
\ 'Num signs in ' . a:group . ' at ' . a:line )
endfunction
function! vimspector#test#signs#AssertSignGroupEmpty( group ) abort
let signs = sign_getplaced( '%', {
\ 'group': a:group,
\ } )
return assert_equal( 1,
\ len( signs ),
\ 'Num buffers named %' ) ||
\ assert_equal( 0,
\ len( signs[ 0 ].signs ),
\ 'Num signs in ' . a:group )
endfunction

View file

@ -17,8 +17,6 @@ if !has('terminal')
finish finish
endif endif
source lib/shared.vim
" Run Vim with "arguments" in a new terminal window. " Run Vim with "arguments" in a new terminal window.
" By default uses a size of 20 lines and 75 columns. " By default uses a size of 20 lines and 75 columns.
" Returns the buffer number of the terminal. " Returns the buffer number of the terminal.

View file

@ -126,7 +126,7 @@ endfunc
" When running into the timeout an exception is thrown, thus the function does " When running into the timeout an exception is thrown, thus the function does
" not return. " not return.
func WaitFor(expr, ...) func WaitFor(expr, ...)
let timeout = get(a:000, 0, 5000) let timeout = get(a:000, 0, 10000)
let slept = s:WaitForCommon(a:expr, v:null, timeout) let slept = s:WaitForCommon(a:expr, v:null, timeout)
if slept < 0 if slept < 0
throw 'WaitFor() timed out after ' . timeout . ' msec' throw 'WaitFor() timed out after ' . timeout . ' msec'
@ -134,8 +134,9 @@ func WaitFor(expr, ...)
return slept return slept
endfunc endfunc
" Wait for up to five seconds for "assert" to return zero. "assert" must be a " Wait for up to five seconds for "assert" to return without adding to v:errors.
" (lambda) function containing one assert function. Example: " "assert" must be a (lambda) function containing one assert function.
" Example:
" call WaitForAssert({-> assert_equal("dead", job_status(job)}) " call WaitForAssert({-> assert_equal("dead", job_status(job)})
" "
" A second argument can be used to specify a different timeout in msec. " A second argument can be used to specify a different timeout in msec.
@ -160,13 +161,15 @@ func s:WaitForCommon(expr, assert, timeout)
endif endif
while 1 while 1
let errors_before = len( v:errors )
if type(a:expr) == v:t_func if type(a:expr) == v:t_func
let success = a:expr() let success = a:expr()
elseif type(a:assert) == v:t_func elseif type(a:assert) == v:t_func
let success = a:assert() == 0 let success = a:assert() == 0 && len( v:errors ) == errors_before
else else
let success = eval(a:expr) let success = eval(a:expr)
endif endif
if success if success
return slept return slept
endif endif
@ -174,9 +177,10 @@ func s:WaitForCommon(expr, assert, timeout)
if slept >= a:timeout if slept >= a:timeout
break break
endif endif
if type(a:assert) == v:t_func if type(a:assert) == v:t_func
" Remove the error added by the assert function. " Remove the errors added by the assert function.
call remove(v:errors, -1) call remove(v:errors, -1 * len( v:errors ) - errors_before )
endif endif
sleep 10m sleep 10m

View file

@ -28,7 +28,7 @@
" When debugging a test it can be useful to add messages to v:errors: " When debugging a test it can be useful to add messages to v:errors:
" call add(v:errors, "this happened") " call add(v:errors, "this happened")
set rtp=$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after set rtp=$PWD/lib,$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after
if has('packages') if has('packages')
let &packpath = &rtp let &packpath = &rtp
endif endif
@ -116,6 +116,11 @@ func RunTheTest(test)
try try
let s:test = a:test let s:test = a:test
let s:testid = g:testpath . ':' . a:test let s:testid = g:testpath . ':' . a:test
let test_filesafe = substitute( a:test, ')', '_', 'g' )
let test_filesafe = substitute( test_filesafe, '(', '_', 'g' )
let test_filesafe = substitute( test_filesafe, ',', '_', 'g' )
let test_filesafe = substitute( test_filesafe, ':', '_', 'g' )
let s:testid_filesafe = g:testpath . '_' . test_filesafe
au VimLeavePre * call EarlyExit(s:test) au VimLeavePre * call EarlyExit(s:test)
exe 'call ' . a:test exe 'call ' . a:test
au! VimLeavePre au! VimLeavePre
@ -201,6 +206,12 @@ func AfterTheTest()
call add(s:errors, 'Found errors in ' . s:testid . ':') call add(s:errors, 'Found errors in ' . s:testid . ':')
call extend(s:errors, v:errors) call extend(s:errors, v:errors)
let v:errors = [] let v:errors = []
let log = readfile( expand( '~/.vimspector.log' ) )
let logfile = s:testid_filesafe . '.vimspector.log'
call writefile( log, logfile, 's' )
call add( s:messages, 'Wrote log for failed test: ' . logfile )
call extend( s:messages, log )
endif endif
endfunc endfunc
@ -306,51 +317,7 @@ endif
for s:test in sort(s:tests) for s:test in sort(s:tests)
" Silence, please! " Silence, please!
set belloff=all set belloff=all
let prev_error = ''
let total_errors = []
let run_nr = 1
call RunTheTest(s:test) call RunTheTest(s:test)
" Repeat a flaky test. Give up when:
" - it fails again with the same message
" - it fails five times (with a different mesage)
if len(v:errors) > 0
\ && (index(s:flaky_tests, s:test) >= 0
\ || v:errors[0] =~ s:flaky_errors_re)
while 1
call add(s:messages, 'Found errors in ' . s:testid . ':')
call extend(s:messages, v:errors)
call add(total_errors, 'Run ' . run_nr . ':')
call extend(total_errors, v:errors)
if run_nr == 5 || prev_error == v:errors[0]
call add(total_errors, 'Flaky test failed too often, giving up')
let v:errors = total_errors
break
endif
call add(s:messages, 'Flaky test failed, running it again')
" Flakiness is often caused by the system being very busy. Sleep a
" couple of seconds to have a higher chance of succeeding the second
" time.
sleep 2
let prev_error = v:errors[0]
let v:errors = []
let run_nr += 1
call RunTheTest(s:test)
if len(v:errors) == 0
" Test passed on rerun.
break
endif
endwhile
endif
call AfterTheTest() call AfterTheTest()
endfor endfor

View file

@ -1,15 +1,9 @@
source lib/shared.vim
function! SetUp() function! SetUp()
if exists ( 'g:loaded_vimpector' ) call vimspector#test#setup#SetUpWithMappings( 'HUMAN' )
unlet g:loaded_vimpector endfunction
endif
let g:vimspector_enable_mappings = 'HUMAN' function! ClearDown()
source vimrc call vimspector#test#setup#ClearDown()
" This is a bit of a hack
runtime! plugin/**/*.vim
endfunction endfunction
function! Test_Step_With_Different_Tabpage() function! Test_Step_With_Different_Tabpage()

View file

@ -1 +1,2 @@
simple simple
variables

View file

@ -1,7 +1,11 @@
CXXFLAGS=-g -O0 CXXFLAGS=-g -O0 -std=c++17
simple: simple.cpp .PHONY: all
TARGETS=simple variables
all: $(TARGETS)
clean: clean:
rm -f simple rm -f $(TARGETS)
rm -rf simple.dSYM rm -rf $(TARGETS:%=%.dSYM)

11
tests/testdata/cpp/simple/variables.cpp vendored Normal file
View file

@ -0,0 +1,11 @@
#include <iostream>
int main() {
std::cout << "Hello world!" << std::endl;
int a = 1;
++a;
int& b = a;
++a;
std::cout << "a: " << a << " b: " << b << std::endl;
return 0;
}