@@ -30,6 +30,11 @@ def release_mozlog_lock():
30
30
'browser_cls' , 'browser_kwargs' ])
31
31
32
32
33
+ ExecutorImplementation = namedtuple ('ExecutorImplementation' ,
34
+ ['executor_cls' , 'executor_kwargs' ,
35
+ 'executor_browser_cls' , 'executor_browser_kwargs' ])
36
+
37
+
33
38
class StopFlag :
34
39
"""Synchronization for coordinating a graceful exit."""
35
40
@@ -75,11 +80,13 @@ class TestRunner:
75
80
parent TestRunnerManager process
76
81
:param executor: TestExecutor object that will actually run a test.
77
82
"""
78
- def __init__ (self , logger , command_queue , result_queue , executor , recording ):
83
+ def __init__ (self , logger , command_queue , result_queue , executor_implementation , recording ):
79
84
self .command_queue = command_queue
80
85
self .result_queue = result_queue
81
-
82
- self .executor = executor
86
+ browser = executor_implementation .executor_browser_cls (
87
+ ** executor_implementation .executor_browser_kwargs )
88
+ self .executor = executor_implementation .executor_cls (
89
+ logger , browser , ** executor_implementation .executor_kwargs )
83
90
self .name = mpcontext .get_context ().current_process ().name
84
91
self .logger = logger
85
92
self .recording = recording
@@ -108,6 +115,28 @@ def teardown(self):
108
115
self .command_queue = None
109
116
self .browser = None
110
117
118
+ def switch_executor (self , executor_implementation ):
119
+ assert self .executor is not None
120
+ # reuse the current protocol connection
121
+ protocol = self .executor .protocol
122
+ self .executor .protocol = None
123
+ self .executor .teardown ()
124
+ browser = executor_implementation .executor_browser_cls (
125
+ ** executor_implementation .executor_browser_kwargs )
126
+ self .executor = executor_implementation .executor_cls (
127
+ self .logger , browser , ** executor_implementation .executor_kwargs )
128
+ if type (self .executor .protocol ) is not type (protocol ):
129
+ self .send_message ("switch_executor_failed" )
130
+ self .logger .error ("Protocol type does not match, switch executor failed." )
131
+ return
132
+ try :
133
+ self .executor .setup (self , protocol )
134
+ except Exception :
135
+ self .send_message ("switch_executor_failed" )
136
+ else :
137
+ self .send_message ("switch_executor_succeeded" )
138
+ self .logger .debug ("Switch Executor done" )
139
+
111
140
def run (self ):
112
141
"""Main loop accepting commands over the pipe and triggering
113
142
the associated methods"""
@@ -118,6 +147,7 @@ def run(self):
118
147
traceback .format_exc ())
119
148
raise
120
149
commands = {"run_test" : self .run_test ,
150
+ "switch_executor" : self .switch_executor ,
121
151
"reset" : self .reset ,
122
152
"stop" : self .stop ,
123
153
"wait" : self .wait }
@@ -157,9 +187,8 @@ def send_message(self, command, *args):
157
187
158
188
159
189
def start_runner (runner_command_queue , runner_result_queue ,
160
- executor_cls , executor_kwargs ,
161
- executor_browser_cls , executor_browser_kwargs ,
162
- capture_stdio , stop_flag , recording ):
190
+ executor_implementation , capture_stdio ,
191
+ stop_flag , recording ):
163
192
"""Launch a TestRunner in a new process"""
164
193
165
194
def send_message (command , * args ):
@@ -179,9 +208,11 @@ def handle_error(e):
179
208
180
209
with capture .CaptureIO (logger , capture_stdio ):
181
210
try :
182
- browser = executor_browser_cls (** executor_browser_kwargs )
183
- executor = executor_cls (logger , browser , ** executor_kwargs )
184
- with TestRunner (logger , runner_command_queue , runner_result_queue , executor , recording ) as runner :
211
+ with TestRunner (logger ,
212
+ runner_command_queue ,
213
+ runner_result_queue ,
214
+ executor_implementation ,
215
+ recording ) as runner :
185
216
try :
186
217
runner .run ()
187
218
except KeyboardInterrupt :
@@ -301,6 +332,8 @@ class _RunnerManagerState:
301
332
running = namedtuple ("running" , ["subsuite" , "test_type" , "test" , "test_group" , "group_metadata" ])
302
333
restarting = namedtuple ("restarting" , ["subsuite" , "test_type" , "test" , "test_group" ,
303
334
"group_metadata" , "force_stop" ])
335
+ switching_executor = namedtuple ("switching_executor" ,
336
+ ["subsuite" , "test_type" , "test" , "test_group" , "group_metadata" ])
304
337
error = namedtuple ("error" , [])
305
338
stop = namedtuple ("stop" , ["force_stop" ])
306
339
@@ -482,6 +515,11 @@ def wait_event(self):
482
515
"test_ended" : self .test_ended ,
483
516
"wait_finished" : self .wait_finished ,
484
517
},
518
+ RunnerManagerState .switching_executor :
519
+ {
520
+ "switch_executor_succeeded" : self .switch_executor_succeeded ,
521
+ "switch_executor_failed" : self .switch_executor_failed ,
522
+ },
485
523
RunnerManagerState .restarting : {},
486
524
RunnerManagerState .error : {},
487
525
RunnerManagerState .stop : {},
@@ -589,18 +627,11 @@ def start_test_runner(self):
589
627
assert self .remote_queue is not None
590
628
self .logger .info ("Starting runner" )
591
629
impl = self .test_implementations [(self .state .subsuite , self .state .test_type )]
592
- self .executor_cls = impl .executor_cls
593
- self .executor_kwargs = impl .executor_kwargs
594
- self .executor_kwargs ["group_metadata" ] = self .state .group_metadata
595
- self .executor_kwargs ["browser_settings" ] = self .browser .browser_settings
596
- executor_browser_cls , executor_browser_kwargs = self .browser .browser .executor_browser ()
630
+ self .executor_implementation = self .get_executor_implementation (impl )
597
631
598
632
args = (self .remote_queue ,
599
633
self .command_queue ,
600
- self .executor_cls ,
601
- self .executor_kwargs ,
602
- executor_browser_cls ,
603
- executor_browser_kwargs ,
634
+ self .executor_implementation ,
604
635
self .capture_stdio ,
605
636
self .child_stop_flag ,
606
637
self .recording )
@@ -613,6 +644,16 @@ def start_test_runner(self):
613
644
self .logger .debug ("Test runner started" )
614
645
# Now we wait for either an init_succeeded event or an init_failed event
615
646
647
+ def get_executor_implementation (self , impl ):
648
+ executor_kwargs = impl .executor_kwargs
649
+ executor_kwargs ["group_metadata" ] = self .state .group_metadata
650
+ executor_kwargs ["browser_settings" ] = self .browser .browser_settings
651
+ executor_browser_cls , executor_browser_kwargs = self .browser .browser .executor_browser ()
652
+ return ExecutorImplementation (impl .executor_cls ,
653
+ executor_kwargs ,
654
+ executor_browser_cls ,
655
+ executor_browser_kwargs )
656
+
616
657
def init_succeeded (self ):
617
658
assert isinstance (self .state , RunnerManagerState .initializing )
618
659
self .browser .after_init ()
@@ -671,8 +712,9 @@ def run_test(self):
671
712
# Factor of 3 on the extra timeout here is based on allowing the executor
672
713
# at least test.timeout + 2 * extra_timeout to complete,
673
714
# which in turn is based on having several layers of timeout inside the executor
674
- wait_timeout = (self .state .test .timeout * self .executor_kwargs ['timeout_multiplier' ] +
675
- 3 * self .executor_cls .extra_timeout )
715
+ timeout_multiplier = self .executor_implementation .executor_kwargs ['timeout_multiplier' ]
716
+ wait_timeout = (self .state .test .timeout * timeout_multiplier +
717
+ 3 * self .executor_implementation .executor_cls .extra_timeout )
676
718
self .timer = threading .Timer (wait_timeout , self ._timeout )
677
719
self .timer .name = f"{ self .name } -timeout"
678
720
@@ -796,7 +838,8 @@ def test_ended(self, test, results):
796
838
test .min_assertion_count ,
797
839
test .max_assertion_count )
798
840
799
- file_result .extra ["test_timeout" ] = test .timeout * self .executor_kwargs ['timeout_multiplier' ]
841
+ timeout_multiplier = self .executor_implementation .executor_kwargs ['timeout_multiplier' ]
842
+ file_result .extra ["test_timeout" ] = test .timeout * timeout_multiplier
800
843
if self .browser .browser_pid :
801
844
file_result .extra ["browser_pid" ] = self .browser .browser_pid
802
845
@@ -832,6 +875,23 @@ def wait_finished(self, rerun=False):
832
875
# post-stop processing
833
876
return self .after_test_end (self .state .test , not rerun , force_rerun = rerun )
834
877
878
+ def switch_executor_succeeded (self ):
879
+ assert isinstance (self .state , RunnerManagerState .switching_executor )
880
+ return RunnerManagerState .running (self .state .subsuite ,
881
+ self .state .test_type ,
882
+ self .state .test ,
883
+ self .state .test_group ,
884
+ self .state .group_metadata )
885
+
886
+ def switch_executor_failed (self ):
887
+ assert isinstance (self .state , RunnerManagerState .switching_executor )
888
+ return RunnerManagerState .restarting (self .state .subsuite ,
889
+ self .state .test_type ,
890
+ self .state .test ,
891
+ self .state .test_group ,
892
+ self .state .group_metadata ,
893
+ False )
894
+
835
895
def after_test_end (self , test , restart , force_rerun = False , force_stop = False ):
836
896
assert isinstance (self .state , RunnerManagerState .running )
837
897
# Mixing manual reruns and automatic reruns is confusing; we currently assume
@@ -844,12 +904,20 @@ def after_test_end(self, test, restart, force_rerun=False, force_stop=False):
844
904
if subsuite != self .state .subsuite :
845
905
self .logger .info (f"Restarting browser for new subsuite:{ subsuite } " )
846
906
restart = True
847
- elif test_type != self .state .test_type :
848
- self .logger .info (f"Restarting browser for new test type:{ test_type } " )
849
- restart = True
850
907
elif self .restart_on_new_group and test_group is not self .state .test_group :
851
908
self .logger .info ("Restarting browser for new test group" )
852
909
restart = True
910
+ elif test_type != self .state .test_type :
911
+ if self .browser .browser .restart_on_test_type_change (test_type , self .state .test_type ):
912
+ self .logger .info (f"Restarting browser for new test type:{ test_type } " )
913
+ restart = True
914
+ else :
915
+ self .logger .info (f"Switching executor for new test type: { self .state .test_type } => { test_type } " )
916
+ impl = self .test_implementations [subsuite , test_type ]
917
+ self .executor_implementation = self .get_executor_implementation (impl )
918
+ self .send_message ("switch_executor" , self .executor_implementation )
919
+ return RunnerManagerState .switching_executor (
920
+ subsuite , test_type , test , test_group , group_metadata )
853
921
else :
854
922
subsuite = self .state .subsuite
855
923
test_type = self .state .test_type
0 commit comments