5
5
import logging
6
6
from abc import ABC , abstractmethod
7
7
from pathlib import Path
8
- from typing import Dict , Optional , cast
8
+ from typing import TYPE_CHECKING , Dict , Optional , cast
9
9
10
10
from hive .client import Client , ClientType
11
11
15
15
from ethereum_test_forks import Fork
16
16
from pytest_plugins .consume .simulators .helpers .ruleset import ruleset
17
17
18
+ if TYPE_CHECKING :
19
+ from .test_tracker import PreAllocGroupTestTracker
20
+
18
21
logger = logging .getLogger (__name__ )
19
22
20
23
@@ -248,12 +251,22 @@ def set_client(self, client: Client) -> None:
248
251
)
249
252
250
253
def stop (self ) -> None :
251
- """Override to log with pre_hash information."""
254
+ """Override to log with pre_hash information and actually stop the client ."""
252
255
if self ._is_started :
253
256
logger .info (
254
- f"Marking multi-test client ({ self .client_type .name } ) for pre-allocation group "
255
- f"{ self .pre_hash } as stopped after { self .test_count } tests"
257
+ f"Stopping multi-test client ({ self .client_type .name } ) for pre-allocation group "
258
+ f"{ self .pre_hash } after { self .test_count } tests"
256
259
)
260
+ # Actually stop the Hive client
261
+ if self .client is not None :
262
+ try :
263
+ self .client .stop ()
264
+ logger .debug (f"Hive client stopped for pre-allocation group { self .pre_hash } " )
265
+ except Exception as e :
266
+ logger .error (
267
+ f"Error stopping Hive client for pre-allocation group { self .pre_hash } : { e } "
268
+ )
269
+
257
270
self .client = None
258
271
self ._is_started = False
259
272
@@ -283,6 +296,7 @@ def __init__(self) -> None:
283
296
284
297
self .multi_test_clients : Dict [str , MultiTestClient ] = {}
285
298
self .pre_alloc_path : Optional [Path ] = None
299
+ self .test_tracker : Optional ["PreAllocGroupTestTracker" ] = None
286
300
self ._initialized = True
287
301
logger .info ("MultiTestClientManager initialized" )
288
302
@@ -297,6 +311,17 @@ def set_pre_alloc_path(self, path: Path) -> None:
297
311
self .pre_alloc_path = path
298
312
logger .debug (f"Pre-alloc path set to: { path } " )
299
313
314
+ def set_test_tracker (self , test_tracker : "PreAllocGroupTestTracker" ) -> None :
315
+ """
316
+ Set the test tracker for automatic client cleanup.
317
+
318
+ Args:
319
+ test_tracker: The test tracker instance
320
+
321
+ """
322
+ self .test_tracker = test_tracker
323
+ logger .debug ("Test tracker set for automatic client cleanup" )
324
+
300
325
def load_pre_alloc_group (self , pre_hash : str ) -> PreAllocGroup :
301
326
"""
302
327
Load the pre-allocation group for a given preHash.
@@ -373,12 +398,15 @@ def get_or_create_multi_test_client(
373
398
374
399
return multi_test_client
375
400
376
- def get_client_for_test (self , pre_hash : str ) -> Optional [Client ]:
401
+ def get_client_for_test (
402
+ self , pre_hash : str , test_id : Optional [str ] = None
403
+ ) -> Optional [Client ]:
377
404
"""
378
405
Get the actual client instance for a test with the given preHash.
379
406
380
407
Args:
381
408
pre_hash: The hash identifying the pre-allocation group
409
+ test_id: Optional test ID for completion tracking
382
410
383
411
Returns:
384
412
The client instance if available, None otherwise
@@ -391,6 +419,58 @@ def get_client_for_test(self, pre_hash: str) -> Optional[Client]:
391
419
return multi_test_client .client
392
420
return None
393
421
422
+ def mark_test_completed (self , pre_hash : str , test_id : str ) -> None :
423
+ """
424
+ Mark a test as completed and trigger automatic client cleanup if appropriate.
425
+
426
+ Args:
427
+ pre_hash: The hash identifying the pre-allocation group
428
+ test_id: The unique test identifier
429
+
430
+ """
431
+ if self .test_tracker is None :
432
+ logger .debug ("No test tracker available, skipping completion tracking" )
433
+ return
434
+
435
+ # Mark test as completed in tracker
436
+ is_group_complete = self .test_tracker .mark_test_completed (pre_hash , test_id )
437
+
438
+ if is_group_complete :
439
+ # All tests in this pre-allocation group are complete
440
+ self ._auto_stop_client_if_complete (pre_hash )
441
+
442
+ def _auto_stop_client_if_complete (self , pre_hash : str ) -> None :
443
+ """
444
+ Automatically stop the client for a pre-allocation group if all tests are complete.
445
+
446
+ Args:
447
+ pre_hash: The hash identifying the pre-allocation group
448
+
449
+ """
450
+ if pre_hash not in self .multi_test_clients :
451
+ logger .debug (f"No client found for pre-allocation group { pre_hash } " )
452
+ return
453
+
454
+ multi_test_client = self .multi_test_clients [pre_hash ]
455
+ if not multi_test_client .is_running :
456
+ logger .debug (f"Client for pre-allocation group { pre_hash } is already stopped" )
457
+ return
458
+
459
+ # Stop the client and remove from tracking
460
+ logger .info (
461
+ f"Auto-stopping client for pre-allocation group { pre_hash } - "
462
+ f"all tests completed ({ multi_test_client .test_count } tests executed)"
463
+ )
464
+
465
+ try :
466
+ multi_test_client .stop ()
467
+ except Exception as e :
468
+ logger .error (f"Error auto-stopping client for pre-allocation group { pre_hash } : { e } " )
469
+ finally :
470
+ # Remove from tracking to free memory
471
+ del self .multi_test_clients [pre_hash ]
472
+ logger .debug (f"Removed completed client from tracking: { pre_hash } " )
473
+
394
474
def stop_all_clients (self ) -> None :
395
475
"""Mark all multi-test clients as stopped."""
396
476
logger .info (f"Marking all { len (self .multi_test_clients )} multi-test clients as stopped" )
@@ -422,4 +502,5 @@ def reset(self) -> None:
422
502
self .stop_all_clients ()
423
503
self .multi_test_clients .clear ()
424
504
self .pre_alloc_path = None
505
+ self .test_tracker = None
425
506
logger .info ("MultiTestClientManager reset" )
0 commit comments