@@ -56,6 +56,8 @@ public class TextBufferIssueTrackerTests
5656    private  IIssueConsumerFactory  issueConsumerFactory ; 
5757    private  IIssueConsumerStorage  issueConsumerStorage ; 
5858    private  IIssueConsumer  issueConsumer ; 
59+     private  ITaskExecutorWithDebounceFactory  taskExecutorWithDebounceFactory ; 
60+     private  ITaskExecutorWithDebounce < ITextSnapshot >  taskExecutorWithDebounce ; 
5961
6062    [ TestInitialize ] 
6163    public  void  SetUp ( ) 
@@ -71,6 +73,9 @@ public void SetUp()
7173        mockDocumentTextBuffer  =  CreateTextBufferMock ( mockTextSnapshot ) ; 
7274        mockedJavascriptDocumentFooJs  =  CreateDocumentMock ( "foo.js" ,  mockDocumentTextBuffer ) ; 
7375        javascriptLanguage  =  [ AnalysisLanguage . Javascript ] ; 
76+         taskExecutorWithDebounceFactory  =  Substitute . For < ITaskExecutorWithDebounceFactory > ( ) ; 
77+         taskExecutorWithDebounce  =  Substitute . For < ITaskExecutorWithDebounce < ITextSnapshot > > ( ) ; 
78+         taskExecutorWithDebounceFactory . Create < ITextSnapshot > ( Arg . Any < double > ( ) ) . Returns ( taskExecutorWithDebounce ) ; 
7479        MockIssueConsumerFactory ( mockedJavascriptDocumentFooJs ,  issueConsumer ) ; 
7580
7681        testSubject  =  CreateTestSubject ( mockedJavascriptDocumentFooJs ) ; 
@@ -98,6 +103,7 @@ public void Ctor_RegistersEventsTrackerAndFactory()
98103        taggerProvider . ActiveTrackersForTesting . Should ( ) . BeEquivalentTo ( testSubject ) ; 
99104
100105        mockedJavascriptDocumentFooJs . Received ( 1 ) . FileActionOccurred  +=  Arg . Any < EventHandler < TextDocumentFileActionEventArgs > > ( ) ; 
106+         ( ( ITextBuffer2 ) mockDocumentTextBuffer ) . Received ( 1 ) . ChangedOnBackground  +=  Arg . Any < EventHandler < TextContentChangedEventArgs > > ( ) ; 
101107
102108        // Note: the test subject isn't responsible for adding the entry to the buffer.Properties 
103109        // - that's done by the TaggerProvider. 
@@ -196,7 +202,6 @@ public void WhenFileIsLoaded_EventsAreNotRaised()
196202        var  renamedEventHandler  =  Substitute . For < EventHandler < DocumentRenamedEventArgs > > ( ) ; 
197203        var  savedEventHandler  =  Substitute . For < EventHandler < DocumentEventArgs > > ( ) ; 
198204        taggerProvider . OpenDocumentRenamed  +=  renamedEventHandler ; 
199-         taggerProvider . DocumentSaved  +=  savedEventHandler ; 
200205
201206        RaiseFileLoadedEvent ( mockedJavascriptDocumentFooJs ) ; 
202207
@@ -326,6 +331,21 @@ public void UpdateAnalysisState_CriticalException_IsNotSuppressed()
326331            . WithMessage ( "this is a test" ) ; 
327332    } 
328333
334+     [ TestMethod ] 
335+     public  void  OnTextBufferChangedOnBackground_UpdatesAnalysisState ( ) 
336+     { 
337+         var  eventHandler  =  SubscribeToDocumentSaved ( ) ; 
338+         var  newContent  =  "new content" ; 
339+         var  newSnapshot  =  CreateTextSnapshotMock ( newContent ) ; 
340+         var  newAnalysisSnapshot  =  new  AnalysisSnapshot ( mockedJavascriptDocumentFooJs . FilePath ,  newSnapshot ) ; 
341+         MockTaskExecutorWithDebounce ( newSnapshot ) ; 
342+         CreateTestSubject ( mockedJavascriptDocumentFooJs ) ; 
343+ 
344+         RaiseTextBufferChangedOnBackground ( currentTextBuffer :  mockDocumentTextBuffer ,  newSnapshot ) ; 
345+ 
346+         VerifyAnalysisStateUpdated ( mockedJavascriptDocumentFooJs ,  newAnalysisSnapshot ,  eventHandler ,  newContent ) ; 
347+     } 
348+ 
329349    private  void  SetUpIssueConsumerStorageThrows ( Exception  exception )  =>  issueConsumerStorage . When ( x =>  x . Remove ( Arg . Any < string > ( ) ) ) . Do ( x =>  throw  exception ) ; 
330350
331351    private  static void  VerifySingletonManagerDoesNotExist ( ITextBuffer  buffer )  =>  FindSingletonManagerInPropertyCollection ( buffer ) . Should ( ) . BeNull ( ) ; 
@@ -372,21 +392,21 @@ private TaggerProvider CreateTaggerProvider()
372392        var  analysisRequester  =  mockAnalysisRequester ; 
373393        var  provider  =  new  TaggerProvider ( sonarErrorListDataSource ,  textDocumentFactoryService , 
374394            serviceProvider ,  languageRecognizer ,  analysisRequester ,  vsProjectInfoProvider ,  issueConsumerFactory ,  issueConsumerStorage ,  Mock . Of < ITaggableBufferIndicator > ( ) , 
375-             mockFileTracker ,  analyzer ,  logger ,  new  InitializationProcessorFactory ( Substitute . For < IAsyncLockFactory > ( ) ,  new  NoOpThreadHandler ( ) ,  new  TestLogger ( ) ) ) ; 
395+             mockFileTracker ,  analyzer ,  logger ,  new  InitializationProcessorFactory ( Substitute . For < IAsyncLockFactory > ( ) ,  new  NoOpThreadHandler ( ) ,  new  TestLogger ( ) ) ,   taskExecutorWithDebounceFactory ) ; 
376396        return  provider ; 
377397    } 
378398
379-     private  static ITextSnapshot  CreateTextSnapshotMock ( ) 
399+     private  static ITextSnapshot  CreateTextSnapshotMock ( string   content   =   TextContent ) 
380400    { 
381401        var  textSnapshot  =  Substitute . For < ITextSnapshot > ( ) ; 
382-         textSnapshot . GetText ( ) . Returns ( TextContent ) ; 
402+         textSnapshot . GetText ( ) . Returns ( content ) ; 
383403        return  textSnapshot ; 
384404    } 
385405
386406    private  static ITextBuffer  CreateTextBufferMock ( ITextSnapshot  textSnapshot ) 
387407    { 
388408        // Text buffer with a properties collection and current snapshot 
389-         var  mockTextBuffer  =  Substitute . For < ITextBuffer > ( ) ; 
409+         var  mockTextBuffer  =  Substitute . For < ITextBuffer2 > ( ) ; 
390410
391411        var  dummyProperties  =  new  PropertyCollection ( ) ; 
392412        mockTextBuffer . Properties . Returns ( dummyProperties ) ; 
@@ -446,12 +466,51 @@ private TextBufferIssueTracker CreateTestSubject(ITextDocument textDocument)
446466    private  TextBufferIssueTracker  CreateTestSubject ( ITextDocument  textDocument ,  ILogger  logger )  => 
447467        new ( taggerProvider , 
448468            textDocument ,  javascriptLanguage , 
449-             mockSonarErrorDataSource ,  vsProjectInfoProvider ,  issueConsumerFactory ,  issueConsumerStorage ,  logger ) ; 
469+             mockSonarErrorDataSource ,  vsProjectInfoProvider ,  issueConsumerFactory ,  issueConsumerStorage ,  taskExecutorWithDebounce ,   logger ) ; 
450470
451471    private  void  ClearIssueConsumerCalls ( ) 
452472    { 
453473        issueConsumerStorage . ClearReceivedCalls ( ) ; 
454474        issueConsumerFactory . ClearReceivedCalls ( ) ; 
455475        vsProjectInfoProvider . ClearReceivedCalls ( ) ; 
456476    } 
477+ 
478+     private  static void  RaiseTextBufferChangedOnBackground ( ITextBuffer  currentTextBuffer ,  ITextSnapshot  newTextSnapshot ) 
479+     { 
480+         var  args  =  new  TextContentChangedEventArgs ( Substitute . For < ITextSnapshot > ( ) ,  newTextSnapshot ,  EditOptions . DefaultMinimalChange ,  null ) ; 
481+         ( ( ITextBuffer2 ) currentTextBuffer ) . ChangedOnBackground  +=  Raise . EventWith ( null ,  args ) ; 
482+     } 
483+ 
484+     private  EventHandler < DocumentEventArgs >  SubscribeToDocumentSaved ( ) 
485+     { 
486+         var  eventHandler  =  Substitute . For < EventHandler < DocumentEventArgs > > ( ) ; 
487+         taggerProvider . DocumentUpdated  +=  eventHandler ; 
488+         return  eventHandler ; 
489+     } 
490+ 
491+     private  void  MockTaskExecutorWithDebounce ( ITextSnapshot  newSnapshot )  => 
492+         taskExecutorWithDebounce . When ( x =>  x . DebounceAsync ( Arg . Is < ITextSnapshot > ( x =>  x  ==  newSnapshot ) ,  Arg . Any < Action < ITextSnapshot > > ( ) ) ) . Do ( callInfo => 
493+         { 
494+             var  action  =  callInfo . Arg < Action < ITextSnapshot > > ( ) ; 
495+             action ( newSnapshot ) ; 
496+         } ) ; 
497+ 
498+     private  void  VerifyAnalysisStateUpdated ( 
499+         ITextDocument  textDocument , 
500+         AnalysisSnapshot  newAnalysisSnapshot , 
501+         EventHandler < DocumentEventArgs >  eventHandler , 
502+         string  newContent ) 
503+     { 
504+         issueConsumerStorage . Received ( ) . Remove ( textDocument . FilePath ) ; 
505+         vsProjectInfoProvider . Received ( ) . GetDocumentProjectInfo ( newAnalysisSnapshot . FilePath ) ; 
506+         issueConsumerFactory . Received ( ) . Create ( textDocument ,  newAnalysisSnapshot . FilePath ,  newAnalysisSnapshot . TextSnapshot ,  Arg . Any < string > ( ) ,  Arg . Any < Guid > ( ) , 
507+             Arg . Any < SnapshotChangedHandler > ( ) ) ; 
508+         issueConsumerStorage . Received ( ) . Set ( textDocument . FilePath ,  Arg . Any < IIssueConsumer > ( ) ) ; 
509+         issueConsumer . Received ( ) . SetIssues ( textDocument . FilePath ,  [ ] ) ; 
510+         issueConsumer . Received ( ) . SetHotspots ( textDocument . FilePath ,  [ ] ) ; 
511+         eventHandler . Received ( ) . Invoke ( taggerProvider , 
512+             Arg . Is < DocumentEventArgs > ( x =>  x . Document . FullPath  ==  textDocument . FilePath 
513+                                            &&  x . Document . DetectedLanguages  ==  javascriptLanguage 
514+                                            &&  x . Content  ==  newContent ) ) ; 
515+     } 
457516} 
0 commit comments