From e9e0e9e5b9c37aa4fcd19c774fff6be6717480c4 Mon Sep 17 00:00:00 2001 From: Ben Jackson Date: Wed, 18 Nov 2020 22:44:18 +0000 Subject: [PATCH] Test for new thread creation - don't clear the stack trace on continue - track running status properly (ish) - mark threads (running) when the app is executing - indicate the "current" thread with a different icon TODO: - allow user to specify current thread? - track running status of threads individually? - allow to pause/continue specific threads? --- python3/vimspector/debug_session.py | 41 +++- python3/vimspector/stack_trace.py | 148 ++++++------ run_tests | 2 +- tests/lib/plugin/shared.vim | 28 +++ tests/stack_trace.test.vim | 322 +++++++++++++++++++++++++- tests/testdata/cpp/simple/threads.cpp | 41 +++- tests/variables.test.vim | 23 +- 7 files changed, 495 insertions(+), 110 deletions(-) diff --git a/python3/vimspector/debug_session.py b/python3/vimspector/debug_session.py index 4f1ef0b..683c515 100644 --- a/python3/vimspector/debug_session.py +++ b/python3/vimspector/debug_session.py @@ -429,6 +429,9 @@ class DebugSession( object ): }, } ) + self._stackTraceView.OnContinued() + self._codeView.SetCurrentFrame( None ) + @IfConnected() def StepInto( self ): if self._stackTraceView.GetCurrentThreadId() is None: @@ -440,6 +443,8 @@ class DebugSession( object ): 'threadId': self._stackTraceView.GetCurrentThreadId() }, } ) + self._stackTraceView.OnContinued() + self._codeView.SetCurrentFrame( None ) @IfConnected() def StepOut( self ): @@ -452,16 +457,39 @@ class DebugSession( object ): 'threadId': self._stackTraceView.GetCurrentThreadId() }, } ) + self._stackTraceView.OnContinued() + self._codeView.SetCurrentFrame( None ) def Continue( self ): - if self._connection: - self._stackTraceView.Continue() - else: + if not self._connection: self.Start() + return + + if self._stackTraceView.GetCurrentThreadId() is None: + utils.UserMessage( 'No current thread', persist = True ) + return + + self._connection.DoRequest( None, { + 'command': 'continue', + 'arguments': { + 'threadId': self._stackTraceView.GetCurrentThreadId(), + }, + } ) + self._stackTraceView.OnContinued() + self._codeView.SetCurrentFrame( None ) @IfConnected() def Pause( self ): - self._stackTraceView.Pause() + if self._stackTraceView.GetCurrentThreadId() is None: + utils.UserMessage( 'No current thread', persist = True ) + return + + self._connection.DoRequest( None, { + 'command': 'pause', + 'arguments': { + 'threadId': self._stackTraceView.GetCurrentThreadId(), + }, + } ) @IfConnected() def ExpandVariable( self ): @@ -1098,6 +1126,7 @@ class DebugSession( object ): def OnEvent_exited( self, message ): utils.UserMessage( 'The debugee exited with status code: {}'.format( message[ 'body' ][ 'exitCode' ] ) ) + self.SetCurrentFrame( None ) def OnEvent_process( self, message ): utils.UserMessage( 'The debugee was started: {}'.format( @@ -1107,7 +1136,8 @@ class DebugSession( object ): pass def OnEvent_continued( self, message ): - pass + self._stackTraceView.OnContinued() + self._codeView.SetCurrentFrame( None ) def Clear( self ): self._codeView.Clear() @@ -1142,6 +1172,7 @@ class DebugSession( object ): def OnEvent_terminated( self, message ): # We will handle this when the server actually exists utils.UserMessage( "Debugging was terminated by the server." ) + self.SetCurrentFrame( None ) def OnEvent_output( self, message ): if self._outputView: diff --git a/python3/vimspector/stack_trace.py b/python3/vimspector/stack_trace.py index 584370f..1634a82 100644 --- a/python3/vimspector/stack_trace.py +++ b/python3/vimspector/stack_trace.py @@ -20,7 +20,30 @@ import logging from vimspector import utils +# TODO: Need to do something a bit like the Variables stuff +# +# class Thread: +# PAUSED = 0 +# RUNNING = 1 +# state = RUNNING +# +# thread: dict +# stacktrace: list +# +# def __init__( self, thread ): +# self.thread = thread +# self.stacktrace = None +# +# def ShouldExpand( self, current_thread_id ): +# return self.thread[ 'id' ] == current_thread_id + + class StackTraceView( object ): + class ThreadRequestState: + NO = 0 + REQUESTING = 1 + PENDING = 2 + def __init__( self, session, win ): self._logger = logging.getLogger( __name__ ) utils.SetUpLogging( self._logger ) @@ -48,14 +71,8 @@ class StackTraceView( object ): self._line_to_frame = {} self._line_to_thread = {} - # TODO: We really need a proper state model - # - # AWAIT_CONNECTION -- OnServerReady / RequestThreads --> REQUESTING_THREADS - # REQUESTING -- OnGotThreads / RequestScopes --> REQUESTING_SCOPES - # - # When we attach using gdbserver, this whole thing breaks because we request - # the threads over and over and get duff data back on later threads. - self._requesting_threads = False + self._requesting_threads = StackTraceView.ThreadRequestState.NO + self._pending_thread_request = None def GetCurrentThreadId( self ): @@ -75,11 +92,12 @@ class StackTraceView( object ): def ConnectionUp( self, connection ): self._connection = connection - self._requesting_threads = False def ConnectionClosed( self ): self.Clear() self._connection = None + self._requesting_threads = StackTraceView.ThreadRequestState.NO + self._pending_thread_request = None def Reset( self ): self.Clear() @@ -89,64 +107,82 @@ class StackTraceView( object ): self._scratch_buffers = [] self._buf = None + self._requesting_threads = StackTraceView.ThreadRequestState.NO + self._pending_thread_request = None - def LoadThreads( self, infer_current_frame ): - pending_request = False - if self._requesting_threads: - pending_request = True + def LoadThreads( self, infer_current_frame, reason = '' ): + if self._requesting_threads != StackTraceView.ThreadRequestState.NO: + self._requesting_threads = StackTraceView.ThreadRequestState.PENDING + self._pending_thread_request = ( infer_current_frame, reason ) return def consume_threads( message ): - self._requesting_threads = False + if self._requesting_threads == StackTraceView.ThreadRequestState.PENDING: + # We may have hit a thread event, so try again. + self._requesting_threads = StackTraceView.ThreadRequestState.NO + self.LoadThreads( *self._pending_thread_request ) + return if not message[ 'body' ][ 'threads' ]: - if pending_request: - # We may have hit a thread event, so try again. - self.LoadThreads( infer_current_frame ) - return - else: - # This is a protocol error. It is required to return at least one! - utils.UserMessage( 'Server returned no threads. Is it running?', - persist = True ) + # This is a protocol error. It is required to return at least one! + utils.UserMessage( 'Protocol error: Server returned no threads', + persist = False, + error = True ) + self._requesting_threads = StackTraceView.ThreadRequestState.NO + self._pending_thread_request = None self._threads.clear() + requesting = False for thread in message[ 'body' ][ 'threads' ]: self._threads.append( thread ) if infer_current_frame and thread[ 'id' ] == self._current_thread: - self._LoadStackTrace( thread, True ) + self._LoadStackTrace( thread, True, reason ) + requesting = True elif infer_current_frame and self._current_thread is None: self._current_thread = thread[ 'id' ] - self._LoadStackTrace( thread, True ) + self._LoadStackTrace( thread, True, reason ) + requesting = True - self._DrawThreads() + if not requesting: + self._DrawThreads() def failure_handler( reason, msg ): # Make sure we request them again if the request fails - self._requesting_threads = False + self._requesting_threads = StackTraceView.ThreadRequestState.NO + self._pending_thread_request = None - self._requesting_threads = True + self._requesting_threads = StackTraceView.ThreadRequestState.REQUESTING self._connection.DoRequest( consume_threads, { 'command': 'threads', }, failure_handler ) - def _DrawThreads( self ): + def _DrawThreads( self, running = False ): self._line_to_frame.clear() self._line_to_thread.clear() - with utils.ModifiableScratchBuffer( self._buf ): + with ( utils.ModifiableScratchBuffer( self._buf ), + utils.RestoreCursorPosition() ): utils.ClearBuffer( self._buf ) for thread in self._threads: - icon = '+' if '_frames' not in thread else '-' + if self._current_thread == thread[ 'id' ]: + icon = '^' if '_frames' not in thread else '>' + else: + icon = '+' if '_frames' not in thread else '-' + + # FIXME: We probably need per-thread status here + if running: + status = ' (running)' + else: + status = '' line = utils.AppendToBuffer( self._buf, - '{0} Thread: {1}'.format( icon, thread[ 'name' ] ) ) + f'{icon} Thread: {thread["name"]}{status}' ) self._line_to_thread[ line ] = thread - self._DrawStackTrace( thread ) def _LoadStackTrace( self, @@ -181,8 +217,7 @@ class StackTraceView( object ): thread = self._line_to_thread[ current_line ] if '_frames' in thread: del thread[ '_frames' ] - with utils.RestoreCursorPosition(): - self._DrawThreads() + self._DrawThreads() else: self._LoadStackTrace( thread, False ) @@ -205,51 +240,28 @@ class StackTraceView( object ): else: return do_jump() + def OnContinued( self, threadId = None ): + # FIXME: This tends to create a very flickery stack trace when steppping. + # Maybe we shouldn't remove the frames, but just update the running status? + # for thread in self._threads: + # if threadId is None or thread[ 'id' ] == threadId: + # thread.pop( '_frames', None ) + self._DrawThreads( running=True ) + def OnStopped( self, event ): if 'threadId' in event: self._current_thread = event[ 'threadId' ] elif event.get( 'allThreadsStopped', False ) and self._threads: self._current_thread = self._threads[ 0 ][ 'id' ] - if self._current_thread is not None: - for thread in self._threads: - if thread[ 'id' ] == self._current_thread: - self._LoadStackTrace( thread, True, 'stopped' ) - return - - self.LoadThreads( True ) + self.LoadThreads( True, 'stopped' ) def OnThreadEvent( self, event ): if event[ 'reason' ] == 'started' and self._current_thread is None: self._current_thread = event[ 'threadId' ] self.LoadThreads( True ) - - def Continue( self ): - if self._current_thread is None: - utils.UserMessage( 'No current thread', persist = True ) - return - - self._session._connection.DoRequest( None, { - 'command': 'continue', - 'arguments': { - 'threadId': self._current_thread, - }, - } ) - - self._session.ClearCurrentFrame() - self.LoadThreads( True ) - - def Pause( self ): - if self._current_thread is None: - utils.UserMessage( 'No current thread', persist = True ) - return - - self._session._connection.DoRequest( None, { - 'command': 'pause', - 'arguments': { - 'threadId': self._current_thread, - }, - } ) + else: + self.LoadThreads( False ) def _DrawStackTrace( self, thread ): if '_frames' not in thread: diff --git a/run_tests b/run_tests index 39dc7ec..b0b244b 100755 --- a/run_tests +++ b/run_tests @@ -142,7 +142,7 @@ echo " * BASEDIR_CMD=$BASEDIR_CMD" echo "%SETUP - Building test programs..." set -e pushd tests/testdata/cpp/simple - make clean all + make all popd set +e echo "%DONE - built test programs" diff --git a/tests/lib/plugin/shared.vim b/tests/lib/plugin/shared.vim index d826b1b..5ee640d 100644 --- a/tests/lib/plugin/shared.vim +++ b/tests/lib/plugin/shared.vim @@ -91,3 +91,31 @@ endfunc function! ThisTestIsFlaky() let g:test_is_flaky = v:true endfunction + +function! AssertMatchist( expected, actual ) abort + let ret = assert_equal( len( a:expected ), len( a:actual ) ) + let len = min( [ len( a:expected ), len( a:actual ) ] ) + let idx = 0 + while idx < len + let ret += assert_match( a:expected[ idx ], a:actual[ idx ] ) + let idx += 1 + endwhile + return ret +endfunction + + +function! GetBufLine( buf, start, end = '$' ) + if type( a:start ) != v:t_string && a:start < 0 + let start = getbufinfo( a:buf )[ 0 ].linecount + a:start + else + let start = a:start + endif + + if type( a:end ) != v:t_string && a:end < 0 + let end = getbufinfo( a:buf )[ 0 ].linecount + a:end + else + let end = a:end + endif + + return getbufline( a:buf, start, end ) +endfunction diff --git a/tests/stack_trace.test.vim b/tests/stack_trace.test.vim index 675f061..5629c2d 100644 --- a/tests/stack_trace.test.vim +++ b/tests/stack_trace.test.vim @@ -10,38 +10,338 @@ endfunction function! s:StartDebugging() exe 'edit ' . s:fn - call vimspector#SetLineBreakpoint( s:fn, 13 ) + call vimspector#SetLineBreakpoint( s:fn, 15 ) call vimspector#Launch() - call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 13, 1 ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 15, 1 ) endfunction -function! Test_Multiple_Threads() - call vimspector#SetLineBreakpoint( s:fn, 41 ) - call vimspector#SetLineBreakpoint( s:fn, 51 ) +function! Test_Multiple_Threads_Continue() + + let thread_l = 67 + let notify_l = 74 + + call vimspector#SetLineBreakpoint( s:fn, thread_l ) + call vimspector#SetLineBreakpoint( s:fn, notify_l ) call s:StartDebugging() call vimspector#Continue() " As we step through the thread creation we should get Thread events - call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 41, 1 ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call cursor( 1, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '> Thread: Thread #1', + \ ' .*: threads!main@threads.cpp:' . string( thread_l ) + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ 2 ) + \ ) + \ } ) call vimspector#Continue() - call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 41, 1 ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call cursor( 1, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '> Thread: Thread #1', + \ ' .*: threads!main@threads.cpp:' . string( thread_l ) + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ 2 ) + \ ) + \ } ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ '$', + \ '$' ) + \ ) + \ } ) call vimspector#Continue() - call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 41, 1 ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call cursor( 1, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '> Thread: Thread #1', + \ ' .*: threads!main@threads.cpp:' . string( thread_l ) + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ 2 ) + \ ) + \ } ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #3', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ '$', + \ '$' ) + \ ) + \ } ) call vimspector#Continue() - call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 41, 1 ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call cursor( 1, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '> Thread: Thread #1', + \ ' .*: threads!main@threads.cpp:' . string( thread_l ) + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ 2 ) + \ ) + \ } ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #4', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ '$', + \ '$' ) + \ ) + \ } ) call vimspector#Continue() " This is the last one - call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 41, 1 ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call cursor( 1, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '> Thread: Thread #1', + \ ' .*: threads!main@threads.cpp:' . string( thread_l ) + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ 2 ) + \ ) + \ } ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #5', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ '$', + \ '$' ) + \ ) + \ } ) call vimspector#Continue() " So we break out of the loop - call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, 51, 1 ) + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, notify_l, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '> Thread: Thread #1', + \ ' .*: threads!main@threads.cpp:' . string( notify_l ) + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ 2 ) + \ ) + \ } ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #6', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ '$', + \ '$' ) + \ ) + \ } ) + + call vimspector#ClearBreakpoints() + call vimspector#test#setup#Reset() + %bwipe! +endfunction + +function! Test_Multiple_Threads_Step() + let thread_l = 67 + let thread_n = thread_l + 1 + let notify_l = 74 + + call vimspector#SetLineBreakpoint( s:fn, thread_l ) + call vimspector#SetLineBreakpoint( s:fn, notify_l ) + call s:StartDebugging() + call vimspector#Continue() + + " As we step through the thread creation we should get Thread events + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '> Thread: Thread #1', + \ ' .*: threads!main@threads.cpp:' . string( thread_l ) + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ 1, + \ 2 ) + \ ) + \ } ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_n, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ '$', + \ '$' ) + \ ) + \ } ) + call vimspector#Continue() + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ '$', + \ '$' ) + \ ) + \ } ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_n, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ '+ Thread: Thread #3', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ -1, + \ '$' ) + \ ) + \ } ) + call vimspector#Continue() + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ '+ Thread: Thread #3', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ -1, + \ '$' ) + \ ) + \ } ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_n, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ '+ Thread: Thread #3', + \ '+ Thread: Thread #4', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ -2, + \ '$' ) + \ ) + \ } ) + call vimspector#Continue() + + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ '+ Thread: Thread #3', + \ '+ Thread: Thread #4', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ -2, + \ '$' ) + \ ) + \ } ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_n, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ '+ Thread: Thread #3', + \ '+ Thread: Thread #4', + \ '+ Thread: Thread #5', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ -3, + \ '$' ) + \ ) + \ } ) + call vimspector#Continue() + + + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_l, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ '+ Thread: Thread #3', + \ '+ Thread: Thread #4', + \ '+ Thread: Thread #5', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ -3, + \ '$' ) + \ ) + \ } ) + call vimspector#StepOver() + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, thread_n, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #2', + \ '+ Thread: Thread #3', + \ '+ Thread: Thread #4', + \ '+ Thread: Thread #5', + \ '+ Thread: Thread #6', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ -4, + \ '$' ) + \ ) + \ } ) + call vimspector#Continue() + + + " So we break out of the loop + call vimspector#test#signs#AssertCursorIsAtLineInBuffer( s:fn, notify_l, 1 ) + call WaitForAssert( {-> + \ AssertMatchist( + \ [ + \ '+ Thread: Thread #6', + \ ], + \ GetBufLine( winbufnr( g:vimspector_session_windows.stack_trace ), + \ '$', + \ '$' ) + \ ) + \ } ) call vimspector#ClearBreakpoints() call vimspector#test#setup#Reset() diff --git a/tests/testdata/cpp/simple/threads.cpp b/tests/testdata/cpp/simple/threads.cpp index 45f8f42..445c09e 100644 --- a/tests/testdata/cpp/simple/threads.cpp +++ b/tests/testdata/cpp/simple/threads.cpp @@ -1,6 +1,8 @@ #include +#include #include #include +#include #include #include #include @@ -36,17 +38,40 @@ int main( int argc, char ** argv ) auto eng = std::default_random_engine() ; auto dist = std::uniform_int_distribution( 250, 1000 ); - for ( int i = 0; i < numThreads; ++i ) + std::mutex m; + std::condition_variable v; + bool ready = false; { - using namespace std::chrono_literals; - threads.emplace_back( [&,tnum=i]() { - std::cout << "Started thread " << tnum << '\n'; - std::this_thread::sleep_for( - 5s + std::chrono::milliseconds( dist( eng ) ) ); - std::cout << "Completed thread " << tnum << '\n'; - }); + std::lock_guard l(m); + + std::cout << "Preparing..." << '\n'; + + for ( int i = 0; i < numThreads; ++i ) + { + using namespace std::chrono_literals; + auto tp = [&,tnum=i]() { + // Wait for the go-ahead + { + std::unique_lock l(m); + while (!ready) { + v.wait(l); + } + } + + std::cout << "Started thread " << tnum << '\n'; + std::this_thread::sleep_for( + 5s + std::chrono::milliseconds( dist( eng ) ) ); + std::cout << "Completed thread " << tnum << '\n'; + }; + + threads.emplace_back( tp ); + } + + std::cout << "Ready to go!" << '\n'; + ready = true; } + v.notify_all(); for ( int i = 0; i < numThreads; ++i ) { diff --git a/tests/variables.test.vim b/tests/variables.test.vim index d710864..155e1d9 100644 --- a/tests/variables.test.vim +++ b/tests/variables.test.vim @@ -8,17 +8,6 @@ function! ClearDown() call vimspector#test#setup#ClearDown() endfunction -function! s:assert_match_list( expected, actual ) abort - let ret = assert_equal( len( a:expected ), len( a:actual ) ) - let len = min( [ len( a:expected ), len( a:actual ) ] ) - let idx = 0 - while idx < len - let ret += assert_match( a:expected[ idx ], a:actual[ idx ] ) - let idx += 1 - endwhile - return ret -endfunction - function! s:StartDebugging( ... ) if a:0 == 0 let config = #{ @@ -222,7 +211,7 @@ function! Test_ExpandVariables() call feedkeys( "\", 'xt' ) call WaitForAssert( {-> - \ s:assert_match_list( + \ AssertMatchist( \ [ \ '- Scope: Locals', \ ' \*- t (Test): {...}', @@ -240,7 +229,7 @@ function! Test_ExpandVariables() " Step - stays expanded call vimspector#StepOver() call WaitForAssert( {-> - \ s:assert_match_list( + \ AssertMatchist( \ [ \ '- Scope: Locals', \ ' - t (Test): {...}', @@ -289,7 +278,7 @@ function! Test_ExpandVariables() call setpos( '.', [ 0, 2, 1 ] ) call feedkeys( "\", 'xt' ) call WaitForAssert( {-> - \ s:assert_match_list( + \ AssertMatchist( \ [ \ '- Scope: Locals', \ ' - t (Test): {...}', @@ -389,7 +378,7 @@ function! Test_ExpandWatch() call feedkeys( "\", 'xt' ) call WaitForAssert( {-> - \ s:assert_match_list( + \ AssertMatchist( \ [ \ 'Watches: ----', \ 'Expression: t', @@ -408,7 +397,7 @@ function! Test_ExpandWatch() " Step - stays expanded call vimspector#StepOver() call WaitForAssert( {-> - \ s:assert_match_list( + \ AssertMatchist( \ [ \ 'Watches: ----', \ 'Expression: t', @@ -460,7 +449,7 @@ function! Test_ExpandWatch() call setpos( '.', [ 0, 3, 1 ] ) call feedkeys( "\", 'xt' ) call WaitForAssert( {-> - \ s:assert_match_list( + \ AssertMatchist( \ [ \ 'Watches: ----', \ 'Expression: t',