@@ -80,6 +80,7 @@ type Session = {
8080 getPname : ( ) => Promise < string | undefined > ;
8181 refreshTimer : { onRefresh : ( cb : ( session : Session ) => void ) => void } ;
8282 targetState ?: TargetState ;
83+ canAccessWhileRunning ?: boolean ;
8384} ;
8485
8586type TrackerCallbacks = {
@@ -176,7 +177,7 @@ describe('ComponentViewer', () => {
176177 expect ( vscode . commands . registerCommand ) . toHaveBeenCalledWith ( 'vscode-cmsis-debugger.componentViewer.lockComponent' , expect . any ( Function ) ) ;
177178 expect ( vscode . commands . registerCommand ) . toHaveBeenCalledWith ( 'vscode-cmsis-debugger.componentViewer.unlockComponent' , expect . any ( Function ) ) ;
178179 // tree provider + 2 commands + 5 tracker disposables
179- expect ( context . subscriptions . length ) . toBe ( 9 ) ;
180+ expect ( context . subscriptions . length ) . toBe ( 11 ) ;
180181 } ) ;
181182
182183 it ( 'skips reading scvd files when session or cbuild-run is missing' , async ( ) => {
@@ -270,10 +271,9 @@ describe('ComponentViewer', () => {
270271 await tracker . callbacks . connected ?.( session ) ;
271272
272273 const refreshCallback = ( session . refreshTimer . onRefresh as jest . Mock ) . mock . calls [ 0 ] ?. [ 0 ] ;
273- // expect(refreshCallback).toBeDefined();
274+ expect ( refreshCallback ) . toBeDefined ( ) ;
274275 if ( refreshCallback ) {
275276 await refreshCallback ( session ) ;
276- await refreshCallback ( otherSession ) ;
277277 }
278278
279279 await tracker . callbacks . connected ?.( otherSession ) ;
@@ -284,9 +284,6 @@ describe('ComponentViewer', () => {
284284
285285 ( controller as unknown as { _activeSession ?: Session } ) . _activeSession = session ;
286286 await tracker . callbacks . stackTrace ?.( { session } ) ;
287- await expect ( tracker . callbacks . stackTrace ?.( { session : otherSession } ) as Promise < void > ) . rejects . toThrow (
288- 'Component Viewer: Received stack trace event for session s2 while active session is s1'
289- ) ;
290287 expect ( ( controller as unknown as { _activeSession ?: Session } ) . _activeSession ) . toBe ( session ) ;
291288
292289
@@ -525,6 +522,32 @@ describe('ComponentViewer', () => {
525522 expect ( root . isLocked ) . toBe ( false ) ;
526523 } ) ;
527524
525+ it ( 'toggles periodic updates via commands' , async ( ) => {
526+ const context = extensionContextFactory ( ) ;
527+ const tracker = makeTracker ( ) ;
528+ const controller = new ComponentViewer ( context ) ;
529+ controller . activate ( tracker as unknown as GDBTargetDebugTracker ) ;
530+
531+ const registerCommandMock = asMockedFunction ( vscode . commands . registerCommand ) ;
532+ const enableHandler = registerCommandMock . mock . calls . find ( ( [ command ] ) => command === 'vscode-cmsis-debugger.componentViewer.enablePeriodicUpdate' ) ?. [ 1 ] as
533+ | ( ( ) => Promise < void > | void )
534+ | undefined ;
535+ const disableHandler = registerCommandMock . mock . calls . find ( ( [ command ] ) => command === 'vscode-cmsis-debugger.componentViewer.disablePeriodicUpdate' ) ?. [ 1 ] as
536+ | ( ( ) => Promise < void > | void )
537+ | undefined ;
538+
539+ expect ( enableHandler ) . toBeDefined ( ) ;
540+ expect ( disableHandler ) . toBeDefined ( ) ;
541+
542+ await enableHandler ?.( ) ;
543+ expect ( ( controller as unknown as { _refreshTimerEnabled : boolean } ) . _refreshTimerEnabled ) . toBe ( true ) ;
544+ expect ( componentViewerLogger . info ) . toHaveBeenCalledWith ( 'Component Viewer: Auto refresh enabled' ) ;
545+
546+ await disableHandler ?.( ) ;
547+ expect ( ( controller as unknown as { _refreshTimerEnabled : boolean } ) . _refreshTimerEnabled ) . toBe ( false ) ;
548+ expect ( componentViewerLogger . info ) . toHaveBeenCalledWith ( 'Component Viewer: Auto refresh disabled' ) ;
549+ } ) ;
550+
528551 it ( 'invokes unlock handler and skips lock when no matching instance exists' , async ( ) => {
529552 const context = extensionContextFactory ( ) ;
530553 const tracker = makeTracker ( ) ;
@@ -628,4 +651,80 @@ describe('ComponentViewer', () => {
628651 expect ( ( controller as unknown as { _runningUpdate : boolean } ) . _runningUpdate ) . toBe ( false ) ;
629652 } ) ;
630653
654+ it ( 'shouldUpdateInstances returns false when no instances' , ( ) => {
655+ const controller = new ComponentViewer ( extensionContextFactory ( ) ) ;
656+ const session = makeSession ( 's1' , [ ] , 'stopped' ) ;
657+
658+ ( controller as unknown as { _instances : ComponentViewerInstancesWrapper [ ] } ) . _instances = [ ] ;
659+
660+ const shouldUpdateInstances = ( controller as unknown as { shouldUpdateInstances : ( s : Session ) => boolean } ) . shouldUpdateInstances . bind ( controller ) ;
661+ expect ( shouldUpdateInstances ( session ) ) . toBe ( false ) ;
662+ } ) ;
663+
664+ it ( 'shouldUpdateInstances returns false when target state is unknown' , ( ) => {
665+ const controller = new ComponentViewer ( extensionContextFactory ( ) ) ;
666+ const session = makeSession ( 's1' , [ ] , 'unknown' ) ;
667+
668+ ( controller as unknown as { _instances : ComponentViewerInstancesWrapper [ ] } ) . _instances = [
669+ { componentViewerInstance : instanceFactory ( ) as unknown as ComponentViewerInstancesWrapper [ 'componentViewerInstance' ] , lockState : false , sessionId : 's1' } ,
670+ ] ;
671+
672+ const shouldUpdateInstances = ( controller as unknown as { shouldUpdateInstances : ( s : Session ) => boolean } ) . shouldUpdateInstances . bind ( controller ) ;
673+ expect ( shouldUpdateInstances ( session ) ) . toBe ( false ) ;
674+ } ) ;
675+
676+ it ( 'shouldUpdateInstances returns false when running and refresh disabled' , ( ) => {
677+ const controller = new ComponentViewer ( extensionContextFactory ( ) ) ;
678+ const session = makeSession ( 's1' , [ ] , 'running' ) ;
679+ ( session as unknown as { canAccessWhileRunning : boolean } ) . canAccessWhileRunning = true ;
680+
681+ ( controller as unknown as { _instances : ComponentViewerInstancesWrapper [ ] } ) . _instances = [
682+ { componentViewerInstance : instanceFactory ( ) as unknown as ComponentViewerInstancesWrapper [ 'componentViewerInstance' ] , lockState : false , sessionId : 's1' } ,
683+ ] ;
684+ ( controller as unknown as { _refreshTimerEnabled : boolean } ) . _refreshTimerEnabled = false ;
685+
686+ const shouldUpdateInstances = ( controller as unknown as { shouldUpdateInstances : ( s : Session ) => boolean } ) . shouldUpdateInstances . bind ( controller ) ;
687+ expect ( shouldUpdateInstances ( session ) ) . toBe ( false ) ;
688+ } ) ;
689+
690+ it ( 'shouldUpdateInstances returns false when running without access' , ( ) => {
691+ const controller = new ComponentViewer ( extensionContextFactory ( ) ) ;
692+ const session = makeSession ( 's1' , [ ] , 'running' ) ;
693+ ( session as unknown as { canAccessWhileRunning : boolean } ) . canAccessWhileRunning = false ;
694+
695+ ( controller as unknown as { _instances : ComponentViewerInstancesWrapper [ ] } ) . _instances = [
696+ { componentViewerInstance : instanceFactory ( ) as unknown as ComponentViewerInstancesWrapper [ 'componentViewerInstance' ] , lockState : false , sessionId : 's1' } ,
697+ ] ;
698+ ( controller as unknown as { _refreshTimerEnabled : boolean } ) . _refreshTimerEnabled = true ;
699+
700+ const shouldUpdateInstances = ( controller as unknown as { shouldUpdateInstances : ( s : Session ) => boolean } ) . shouldUpdateInstances . bind ( controller ) ;
701+ expect ( shouldUpdateInstances ( session ) ) . toBe ( false ) ;
702+ } ) ;
703+
704+ it ( 'shouldUpdateInstances returns true when running with refresh and access' , ( ) => {
705+ const controller = new ComponentViewer ( extensionContextFactory ( ) ) ;
706+ const session = makeSession ( 's1' , [ ] , 'running' ) ;
707+ ( session as unknown as { canAccessWhileRunning : boolean } ) . canAccessWhileRunning = true ;
708+
709+ ( controller as unknown as { _instances : ComponentViewerInstancesWrapper [ ] } ) . _instances = [
710+ { componentViewerInstance : instanceFactory ( ) as unknown as ComponentViewerInstancesWrapper [ 'componentViewerInstance' ] , lockState : false , sessionId : 's1' } ,
711+ ] ;
712+ ( controller as unknown as { _refreshTimerEnabled : boolean } ) . _refreshTimerEnabled = true ;
713+
714+ const shouldUpdateInstances = ( controller as unknown as { shouldUpdateInstances : ( s : Session ) => boolean } ) . shouldUpdateInstances . bind ( controller ) ;
715+ expect ( shouldUpdateInstances ( session ) ) . toBe ( true ) ;
716+ } ) ;
717+
718+ it ( 'shouldUpdateInstances returns true when stopped' , ( ) => {
719+ const controller = new ComponentViewer ( extensionContextFactory ( ) ) ;
720+ const session = makeSession ( 's1' , [ ] , 'stopped' ) ;
721+
722+ ( controller as unknown as { _instances : ComponentViewerInstancesWrapper [ ] } ) . _instances = [
723+ { componentViewerInstance : instanceFactory ( ) as unknown as ComponentViewerInstancesWrapper [ 'componentViewerInstance' ] , lockState : false , sessionId : 's1' } ,
724+ ] ;
725+
726+ const shouldUpdateInstances = ( controller as unknown as { shouldUpdateInstances : ( s : Session ) => boolean } ) . shouldUpdateInstances . bind ( controller ) ;
727+ expect ( shouldUpdateInstances ( session ) ) . toBe ( true ) ;
728+ } ) ;
729+
631730} ) ;
0 commit comments