@@ -663,10 +663,66 @@ impl Console {
663663 /// Access a reference to the singleton instance of this struct
664664 ///
665665 /// SAFETY: Accesses must occur after `Console::start()` initializes it.
666- pub ( crate ) fn get ( ) -> & ' static Self {
666+ pub fn get ( ) -> & ' static Self {
667667 Console :: get_mut ( )
668668 }
669669
670+ /// Install a minimal stopgap `Console` in the thread-local for unit tests
671+ /// that need a `&Console` (e.g. to pass to `CommHandler` methods) but
672+ /// don't go through the full `Console::start()` path.
673+ ///
674+ /// All internal channels are created with their counterpart immediately
675+ /// dropped, so the Console is inert: it never processes requests, tasks,
676+ /// or kernel messages. Any attempt to send to IOPub or other channels
677+ /// will return a disconnected error, which surfaces problems early
678+ /// rather than silently hanging.
679+ ///
680+ /// For tests that exercise real Console behaviour (execution, comms
681+ /// lifecycle, IOPub output), prefer full integration tests with
682+ /// `DummyArkFrontend` which spins up a real Console.
683+ ///
684+ /// Idempotent per thread. Does not set `R_INIT`, so the `r_task()` escape
685+ /// hatch for unit tests continues to work.
686+ #[ cfg( feature = "testing" ) ]
687+ pub fn test_init ( ) {
688+ use std:: cell:: Cell ;
689+ thread_local ! {
690+ static INITIALIZED : Cell <bool > = const { Cell :: new( false ) } ;
691+ }
692+
693+ INITIALIZED . with ( |init| {
694+ if init. get ( ) {
695+ return ;
696+ }
697+ init. set ( true ) ;
698+
699+ let ( _, tasks_interrupt_rx) = crossbeam:: channel:: unbounded ( ) ;
700+ let ( _, tasks_idle_rx) = crossbeam:: channel:: unbounded ( ) ;
701+ let ( _, tasks_idle_any_rx) = crossbeam:: channel:: unbounded ( ) ;
702+ let ( comm_event_tx, _) = crossbeam:: channel:: unbounded ( ) ;
703+ let ( r_request_tx, r_request_rx) = crossbeam:: channel:: unbounded ( ) ;
704+ let ( stdin_request_tx, _) = crossbeam:: channel:: unbounded ( ) ;
705+ let ( _, stdin_reply_rx) = crossbeam:: channel:: unbounded ( ) ;
706+ let ( iopub_tx, _) = crossbeam:: channel:: unbounded ( ) ;
707+ let ( _, kernel_request_rx) = crossbeam:: channel:: unbounded ( ) ;
708+ let dap = Dap :: new_shared ( r_request_tx) ;
709+
710+ CONSOLE . set ( UnsafeCell :: new ( Console :: new (
711+ tasks_interrupt_rx,
712+ tasks_idle_rx,
713+ tasks_idle_any_rx,
714+ comm_event_tx,
715+ r_request_rx,
716+ stdin_request_tx,
717+ stdin_reply_rx,
718+ iopub_tx,
719+ kernel_request_rx,
720+ dap,
721+ SessionMode :: Console ,
722+ ) ) ) ;
723+ } ) ;
724+ }
725+
670726 /// Access a mutable reference to the singleton instance of this struct
671727 ///
672728 /// SAFETY: Accesses must occur after `Console::start()` initializes it.
0 commit comments