@@ -16,18 +16,33 @@ public ForceDriverTests (ITestOutputHelper output)
1616 }
1717
1818 /// <summary>
19- /// Tests that ForceDriver persists when opening a scenario after Init/Shutdown cycles .
20- /// This verifies the fix for issue #4391.
19+ /// Tests that ForceDriver persists when running UICatalogTop and then opening a scenario .
20+ /// This simulates the actual UICatalog flow and verifies the fix for issue #4391.
2121 /// </summary>
2222 [ Fact ]
23- public void ForceDriver_Persists_Across_Init_Shutdown_Cycles ( )
23+ public void ForceDriver_Persists_From_UICatalogTop_To_Scenario ( )
2424 {
2525 // Arrange
2626 const string expectedDriver = "fake" ;
2727
2828 ConfigurationManager . Disable ( true ) ;
2929 Application . ResetState ( true ) ;
3030
31+ // Initialize UICatalog.Options (required by UICatalogTop)
32+ global ::UICatalog . UICatalog . Options = new UICatalogCommandLineOptions
33+ {
34+ Driver = expectedDriver ,
35+ DontEnableConfigurationManagement = false ,
36+ Scenario = "none" ,
37+ BenchmarkTimeout = 2500 ,
38+ Benchmark = false ,
39+ ResultsFile = string . Empty ,
40+ DebugLogLevel = "Warning"
41+ } ;
42+
43+ // Initialize cached scenarios (required by UICatalogTop)
44+ UICatalogTop . CachedScenarios = Scenario . GetScenarios ( ) ;
45+
3146 // Set ForceDriver in RuntimeConfig (simulating what UICatalog does with --driver option)
3247 ConfigurationManager . RuntimeConfig = $$ """
3348 {
@@ -38,54 +53,136 @@ public void ForceDriver_Persists_Across_Init_Shutdown_Cycles ()
3853 // Enable ConfigurationManager with all locations (as UICatalog does)
3954 ConfigurationManager . Enable ( ConfigLocations . All ) ;
4055
41- var firstDriverName = string . Empty ;
42- var secondDriverName = string . Empty ;
56+ var topLevelDriverName = string . Empty ;
57+ var scenarioDriverName = string . Empty ;
58+ var iterationCount = 0 ;
59+ EventHandler < IterationEventArgs > ? iterationHandler = null ;
4360
4461 try
4562 {
46- // Act - Cycle 1: Init and check driver
47- _output . WriteLine ( "Cycle 1: First Init " ) ;
63+ // Phase 1: Run UICatalogTop (simulating main UI)
64+ _output . WriteLine ( "=== Phase 1: Running UICatalogTop === " ) ;
4865 Application . Init ( ) ;
49- firstDriverName = Application . Driver ? . GetName ( ) ?? string . Empty ;
50- _output . WriteLine ( $ "Cycle 1 driver: { firstDriverName } ") ;
51- Application . Shutdown ( ) ;
66+ topLevelDriverName = Application . Driver ? . GetName ( ) ?? string . Empty ;
67+ _output . WriteLine ( $ "UICatalogTop driver: { topLevelDriverName } ") ;
5268
53- // Act - Cycle 2: Reload RuntimeConfig and Init again (simulating scenario opening)
54- _output . WriteLine ( "Cycle 2: Reload RuntimeConfig and Init again" ) ;
69+ var top = new UICatalogTop ( ) ;
5570
56- // This simulates what the fix does before each scenario
57- ConfigurationManager . Load ( ConfigLocations . Runtime ) ;
58- ConfigurationManager . Apply ( ) ;
71+ // Set up to automatically select a scenario and stop
72+ iterationHandler = ( sender , e ) =>
73+ {
74+ iterationCount ++ ;
75+
76+ // On first iteration, select a scenario and request stop
77+ if ( iterationCount == 1 )
78+ {
79+ // Select the first scenario
80+ if ( UICatalogTop . CachedScenarios is { Count : > 0 } )
81+ {
82+ UICatalogTop . CachedSelectedScenario =
83+ ( Scenario ) Activator . CreateInstance ( UICatalogTop . CachedScenarios [ 0 ] . GetType ( ) ) ! ;
84+ Application . RequestStop ( ) ;
85+ }
86+ }
87+ } ;
88+
89+ Application . Iteration += iterationHandler ;
90+ Application . Run ( top ) ;
91+ Application . Iteration -= iterationHandler ;
5992
60- // Scenario calls Application.Init() without parameters
61- Application . Init ( ) ;
62- secondDriverName = Application . Driver ? . GetName ( ) ?? string . Empty ;
63- _output . WriteLine ( $ "Cycle 2 driver: { secondDriverName } ") ;
93+ top . Dispose ( ) ;
6494 Application . Shutdown ( ) ;
95+
96+ _output . WriteLine ( $ "Selected scenario: { UICatalogTop . CachedSelectedScenario ? . GetName ( ) } ") ;
97+ _output . WriteLine ( $ "UICatalogTop completed after { iterationCount } iterations") ;
98+
99+ // Phase 2: Run the selected scenario (simulating what UICatalog.cs does)
100+ if ( UICatalogTop . CachedSelectedScenario is { } scenario )
101+ {
102+ _output . WriteLine ( $ "\n === Phase 2: Running scenario '{ scenario . GetName ( ) } ' ===") ;
103+
104+ // Reload RuntimeConfig before scenario (as the fix does)
105+ if ( ! global ::UICatalog . UICatalog . Options . DontEnableConfigurationManagement )
106+ {
107+ ConfigurationManager . Load ( ConfigLocations . Runtime ) ;
108+ ConfigurationManager . Apply ( ) ;
109+ _output . WriteLine ( "Reloaded RuntimeConfig" ) ;
110+ }
111+
112+ // Track the driver used inside the scenario
113+ string ? driverInsideScenario = null ;
114+ EventHandler < EventArgs < bool > > ? initHandler = null ;
115+ EventHandler < IterationEventArgs > ? scenarioIterationHandler = null ;
116+
117+ initHandler = ( s , e ) =>
118+ {
119+ if ( e . Value )
120+ {
121+ driverInsideScenario = Application . Driver ? . GetName ( ) ;
122+
123+ // Request stop immediately so the scenario doesn't actually run
124+ scenarioIterationHandler = ( _ , _ ) =>
125+ {
126+ Application . RequestStop ( ) ;
127+ // Remove immediately to avoid assertions in Shutdown
128+ if ( scenarioIterationHandler != null )
129+ {
130+ Application . Iteration -= scenarioIterationHandler ;
131+ scenarioIterationHandler = null ;
132+ }
133+ } ;
134+ Application . Iteration += scenarioIterationHandler ;
135+ }
136+ } ;
137+
138+ Application . InitializedChanged += initHandler ;
139+
140+ // Run the scenario's Main() method (this is what UICatalog does)
141+ scenario . Main ( ) ;
142+ scenarioDriverName = driverInsideScenario ?? string . Empty ;
143+
144+ Application . InitializedChanged -= initHandler ;
145+ scenario . Dispose ( ) ;
146+
147+ _output . WriteLine ( $ "Scenario driver: { scenarioDriverName } ") ;
148+ _output . WriteLine ( "Scenario completed and disposed" ) ;
149+ }
150+ else
151+ {
152+ _output . WriteLine ( "ERROR: No scenario was selected" ) ;
153+ Assert . Fail ( "No scenario was selected" ) ;
154+ }
65155
66156 // Assert
67- Assert . Equal ( expectedDriver , firstDriverName ) ;
68- Assert . Equal ( expectedDriver , secondDriverName ) ;
69- _output . WriteLine ( $ "SUCCESS: Driver '{ expectedDriver } ' persisted across Init/Shutdown cycles") ;
157+ _output . WriteLine ( $ "\n === Results ===") ;
158+ _output . WriteLine ( $ "UICatalogTop driver: { topLevelDriverName } ") ;
159+ _output . WriteLine ( $ "Scenario driver: { scenarioDriverName } ") ;
160+
161+ Assert . Equal ( expectedDriver , topLevelDriverName ) ;
162+ Assert . Equal ( expectedDriver , scenarioDriverName ) ;
163+ _output . WriteLine ( $ "SUCCESS: Driver '{ expectedDriver } ' persisted from UICatalogTop to scenario") ;
70164 }
71165 finally
72166 {
167+ if ( iterationHandler != null )
168+ {
169+ Application . Iteration -= iterationHandler ;
170+ }
73171 ConfigurationManager . Disable ( true ) ;
74172 Application . ResetState ( true ) ;
75173 }
76174 }
77175
78176 /// <summary>
79- /// Tests that ForceDriver is used when a scenario calls Application.Init() without parameters .
80- /// This simulates the actual UICatalog scenario execution flow .
177+ /// Tests that ForceDriver persists when running multiple scenarios in sequence .
178+ /// This verifies the scenario loop in UICatalog works correctly .
81179 /// </summary>
82180 [ Fact ]
83- public void ForceDriver_Used_By_Scenario_Init ( )
181+ public void ForceDriver_Persists_Across_Multiple_Scenarios ( )
84182 {
85183 // Arrange
86184 const string expectedDriver = "fake" ;
87- Scenario ? scenario = null ;
88-
185+
89186 ConfigurationManager . Disable ( true ) ;
90187 Application . ResetState ( true ) ;
91188
@@ -99,34 +196,106 @@ public void ForceDriver_Used_By_Scenario_Init ()
99196 // Enable ConfigurationManager
100197 ConfigurationManager . Enable ( ConfigLocations . All ) ;
101198
199+ string ? driver1 = null ;
200+ string ? driver2 = null ;
201+ EventHandler < EventArgs < bool > > ? initHandler1 = null ;
202+ EventHandler < EventArgs < bool > > ? initHandler2 = null ;
203+ EventHandler < IterationEventArgs > ? iterHandler1 = null ;
204+ EventHandler < IterationEventArgs > ? iterHandler2 = null ;
205+
102206 try
103207 {
104- // Get the first available scenario
208+ // Get two different scenarios to test
105209 var scenarios = Scenario . GetScenarios ( ) ;
106- Assert . NotEmpty ( scenarios ) ;
210+ Assert . True ( scenarios . Count >= 2 , "Need at least 2 scenarios for this test" ) ;
211+
212+ var scenario1 = scenarios [ 0 ] ;
213+ var scenario2 = scenarios [ 1 ] ;
214+
215+ _output . WriteLine ( $ "Testing with scenarios: { scenario1 . GetName ( ) } and { scenario2 . GetName ( ) } ") ;
216+
217+ // Run scenario 1
218+ initHandler1 = ( s , e ) =>
219+ {
220+ if ( e . Value )
221+ {
222+ driver1 = Application . Driver ? . GetName ( ) ;
223+ iterHandler1 = ( _ , _ ) =>
224+ {
225+ Application . RequestStop ( ) ;
226+ // Remove immediately to avoid assertions in Shutdown
227+ if ( iterHandler1 != null )
228+ {
229+ Application . Iteration -= iterHandler1 ;
230+ iterHandler1 = null ;
231+ }
232+ } ;
233+ Application . Iteration += iterHandler1 ;
234+ }
235+ } ;
107236
108- scenario = scenarios [ 0 ] ;
109- var scenarioName = scenario . GetName ( ) ;
110- _output . WriteLine ( $ "Testing with scenario: { scenarioName } ") ;
237+ Application . InitializedChanged += initHandler1 ;
238+ scenario1 . Main ( ) ;
239+ Application . InitializedChanged -= initHandler1 ;
240+ scenario1 . Dispose ( ) ;
241+ _output . WriteLine ( $ "Scenario 1 completed with driver: { driver1 } ") ;
111242
112- // Reload RuntimeConfig before scenario (as the fix does)
243+ // Reload RuntimeConfig before scenario 2 (as the fix does)
113244 ConfigurationManager . Load ( ConfigLocations . Runtime ) ;
114245 ConfigurationManager . Apply ( ) ;
246+ _output . WriteLine ( "Reloaded RuntimeConfig" ) ;
115247
116- // Scenario calls Application.Init() - it should use ForceDriver
117- Application . Init ( ) ;
118- var driverName = Application . Driver ? . GetName ( ) ?? string . Empty ;
119- _output . WriteLine ( $ "Scenario driver: { driverName } ") ;
248+ // Run scenario 2
249+ initHandler2 = ( s , e ) =>
250+ {
251+ if ( e . Value )
252+ {
253+ driver2 = Application . Driver ? . GetName ( ) ;
254+ iterHandler2 = ( _ , _ ) =>
255+ {
256+ Application . RequestStop ( ) ;
257+ // Remove immediately to avoid assertions in Shutdown
258+ if ( iterHandler2 != null )
259+ {
260+ Application . Iteration -= iterHandler2 ;
261+ iterHandler2 = null ;
262+ }
263+ } ;
264+ Application . Iteration += iterHandler2 ;
265+ }
266+ } ;
267+
268+ Application . InitializedChanged += initHandler2 ;
269+ scenario2 . Main ( ) ;
270+ Application . InitializedChanged -= initHandler2 ;
271+ scenario2 . Dispose ( ) ;
272+ _output . WriteLine ( $ "Scenario 2 completed with driver: { driver2 } ") ;
120273
121274 // Assert
122- Assert . Equal ( expectedDriver , driverName ) ;
123- _output . WriteLine ( $ "SUCCESS: Scenario uses ForceDriver '{ expectedDriver } '") ;
124-
125- Application . Shutdown ( ) ;
275+ Assert . Equal ( expectedDriver , driver1 ) ;
276+ Assert . Equal ( expectedDriver , driver2 ) ;
277+ _output . WriteLine ( $ "SUCCESS: Driver '{ expectedDriver } ' persisted across both scenarios") ;
126278 }
127279 finally
128280 {
129- scenario ? . Dispose ( ) ;
281+ // Cleanup any remaining handlers
282+ if ( initHandler1 != null )
283+ {
284+ Application . InitializedChanged -= initHandler1 ;
285+ }
286+ if ( iterHandler2 != null )
287+ {
288+ Application . InitializedChanged -= initHandler2 ;
289+ }
290+ if ( iterHandler1 != null )
291+ {
292+ Application . Iteration -= iterHandler1 ;
293+ }
294+ if ( iterHandler2 != null )
295+ {
296+ Application . Iteration -= iterHandler2 ;
297+ }
298+
130299 ConfigurationManager . Disable ( true ) ;
131300 Application . ResetState ( true ) ;
132301 }
0 commit comments