@@ -7269,15 +7269,20 @@ def setUp(self, masterqa_mode=False):
7269
7269
self .guest_mode = sb_config .guest_mode
7270
7270
self .devtools = sb_config .devtools
7271
7271
self .remote_debug = sb_config .remote_debug
7272
+ self ._multithreaded = sb_config ._multithreaded
7273
+ self ._reuse_session = sb_config .reuse_session
7274
+ self ._crumbs = sb_config .crumbs
7272
7275
self .dashboard = sb_config .dashboard
7273
7276
self ._dash_initialized = sb_config ._dashboard_initialized
7277
+ if self .dashboard and self ._multithreaded :
7278
+ import fasteners
7279
+ self .dash_lock = fasteners .InterProcessLock (
7280
+ constants .Dashboard .LOCKFILE )
7274
7281
self .swiftshader = sb_config .swiftshader
7275
7282
self .user_data_dir = sb_config .user_data_dir
7276
7283
self .extension_zip = sb_config .extension_zip
7277
7284
self .extension_dir = sb_config .extension_dir
7278
7285
self .maximize_option = sb_config .maximize_option
7279
- self ._reuse_session = sb_config .reuse_session
7280
- self ._crumbs = sb_config .crumbs
7281
7286
self .save_screenshot_after_test = sb_config .save_screenshot
7282
7287
self .visual_baseline = sb_config .visual_baseline
7283
7288
self .timeout_multiplier = sb_config .timeout_multiplier
@@ -7391,13 +7396,21 @@ def setUp(self, masterqa_mode=False):
7391
7396
7392
7397
# Dashboard pre-processing:
7393
7398
if self .dashboard :
7394
- sb_config ._sbase_detected = True
7395
- sb_config ._only_unittest = False
7396
- if not self ._dash_initialized :
7397
- sb_config ._dashboard_initialized = True
7399
+ if self ._multithreaded :
7400
+ with self .dash_lock :
7401
+ sb_config ._sbase_detected = True
7402
+ sb_config ._only_unittest = False
7403
+ if not self ._dash_initialized :
7404
+ sb_config ._dashboard_initialized = True
7405
+ self ._dash_initialized = True
7406
+ self .__process_dashboard (False , init = True )
7407
+ else :
7398
7408
sb_config ._sbase_detected = True
7399
- self ._dash_initialized = True
7400
- self .__process_dashboard (False , init = True )
7409
+ sb_config ._only_unittest = False
7410
+ if not self ._dash_initialized :
7411
+ sb_config ._dashboard_initialized = True
7412
+ self ._dash_initialized = True
7413
+ self .__process_dashboard (False , init = True )
7401
7414
7402
7415
has_url = False
7403
7416
if self ._reuse_session :
@@ -7696,6 +7709,23 @@ def __create_log_path_as_needed(self, test_logpath):
7696
7709
7697
7710
def __process_dashboard (self , has_exception , init = False ):
7698
7711
''' SeleniumBase Dashboard Processing '''
7712
+ existing_res = sb_config ._results # Used by multithreaded tests
7713
+ if self ._multithreaded :
7714
+ abs_path = os .path .abspath ('.' )
7715
+ dash_json_loc = constants .Dashboard .DASH_JSON
7716
+ dash_jsonpath = os .path .join (abs_path , dash_json_loc )
7717
+ if not init and os .path .exists (dash_jsonpath ):
7718
+ with open (dash_jsonpath , 'r' ) as f :
7719
+ dash_json = f .read ().strip ()
7720
+ dash_data , d_id , dash_runtimes , d_stats = json .loads (dash_json )
7721
+ num_passed , num_failed , num_skipped , num_untested = d_stats
7722
+ sb_config ._results = dash_data
7723
+ sb_config ._display_id = d_id
7724
+ sb_config ._duration = dash_runtimes
7725
+ sb_config .item_count_passed = num_passed
7726
+ sb_config .item_count_failed = num_failed
7727
+ sb_config .item_count_skipped = num_skipped
7728
+ sb_config .item_count_untested = num_untested
7699
7729
if len (sb_config ._extra_dash_entries ) > 0 :
7700
7730
# First take care of existing entries from non-SeleniumBase tests
7701
7731
for test_id in sb_config ._extra_dash_entries :
@@ -7741,6 +7771,11 @@ def __process_dashboard(self, has_exception, init=False):
7741
7771
sb_config .item_count_skipped += 1
7742
7772
sb_config .item_count_untested -= 1
7743
7773
sb_config ._results [test_id ] = "Skipped"
7774
+ elif self ._multithreaded and test_id in existing_res .keys () and (
7775
+ existing_res [test_id ] == "Skipped" ):
7776
+ sb_config .item_count_skipped += 1
7777
+ sb_config .item_count_untested -= 1
7778
+ sb_config ._results [test_id ] = "Skipped"
7744
7779
elif has_exception :
7745
7780
# pytest-rerunfailures may cause duplicate results
7746
7781
if test_id not in sb_config ._results .keys () or (
@@ -7778,6 +7813,14 @@ def __process_dashboard(self, has_exception, init=False):
7778
7813
if sb_config ._using_html_report :
7779
7814
# Add the pie chart to the pytest html report
7780
7815
sb_config ._saved_dashboard_pie = self .extract_chart ()
7816
+ if self ._multithreaded :
7817
+ abs_path = os .path .abspath ('.' )
7818
+ dash_pie = json .dumps (sb_config ._saved_dashboard_pie )
7819
+ dash_pie_loc = constants .Dashboard .DASH_PIE
7820
+ pie_path = os .path .join (abs_path , dash_pie_loc )
7821
+ pie_file = codecs .open (pie_path , "w+" , encoding = "utf-8" )
7822
+ pie_file .writelines (dash_pie )
7823
+ pie_file .close ()
7781
7824
head = (
7782
7825
'<head><meta charset="utf-8" />'
7783
7826
'<meta property="og:image" '
@@ -7887,6 +7930,17 @@ def __process_dashboard(self, has_exception, init=False):
7887
7930
out_file .writelines (the_html )
7888
7931
out_file .close ()
7889
7932
time .sleep (0.05 ) # Add time for dashboard server to process updates
7933
+ if self ._multithreaded :
7934
+ d_stats = (num_passed , num_failed , num_skipped , num_untested )
7935
+ _results = sb_config ._results
7936
+ _display_id = sb_config ._display_id
7937
+ _duration = sb_config ._duration
7938
+ dash_json = json .dumps ((_results , _display_id , _duration , d_stats ))
7939
+ dash_json_loc = constants .Dashboard .DASH_JSON
7940
+ dash_jsonpath = os .path .join (abs_path , dash_json_loc )
7941
+ dash_json_file = codecs .open (dash_jsonpath , "w+" , encoding = "utf-8" )
7942
+ dash_json_file .writelines (dash_json )
7943
+ dash_json_file .close ()
7890
7944
7891
7945
def has_exception (self ):
7892
7946
""" (This method should ONLY be used in custom tearDown() methods.)
@@ -8031,7 +8085,11 @@ def tearDown(self):
8031
8085
test_logpath , self .driver ,
8032
8086
self .__last_page_source )
8033
8087
if self .dashboard :
8034
- self .__process_dashboard (has_exception )
8088
+ if self ._multithreaded :
8089
+ with self .dash_lock :
8090
+ self .__process_dashboard (has_exception )
8091
+ else :
8092
+ self .__process_dashboard (has_exception )
8035
8093
# (Pytest) Finally close all open browser windows
8036
8094
self .__quit_all_drivers ()
8037
8095
if self .headless :
0 commit comments