@@ -71,12 +71,43 @@ pub struct NodeInfo {
7171 config_modifier : ConfigModifier < Config > ,
7272}
7373
74+ fn global_slot ( ) -> Option < usize > {
75+ if let Ok ( slot_str) = std:: env:: var ( "NEXTEST_TEST_GLOBAL_SLOT" ) {
76+ Some (
77+ slot_str
78+ . parse :: < usize > ( )
79+ . expect ( "NEXTEST_TEST_GLOBAL_SLOT must be a non-negative integer" ) ,
80+ )
81+ } else {
82+ None
83+ }
84+ }
85+
86+ const BASE_PORT : usize = 5000 ;
87+ const PORTS_PER_NODE : usize = 10 ;
88+ const PORTS_PER_SLOT : usize = 200 ; // ample space for 20 nodes
89+
7490#[ async_trait]
7591impl NodeRunner < TestContext > for TestRunner {
7692 type NodeHandle = Handle ;
7793
7894 fn new < S > ( id : usize , nodes : & [ TestNode < TestContext , S > ] , params : TestParams ) -> Self {
79- let base_port = 20_000 + id * 1000 ;
95+ // Check if the NEXTEST_TEST_GLOBAL_SLOT environment variable is set.
96+ //
97+ // Global slot numbers are non-negative integers starting from 0 that
98+ // are unique within the run for the lifetime of the test,
99+ // but are reused after the test finishes.
100+ //
101+ // We use them to assign ports to nodes in a way that allows multiple tests
102+ // to run in parallel without port conflicts.
103+ let global_slot = global_slot ( ) . unwrap_or ( 0 ) ;
104+
105+ let port_offset = global_slot * PORTS_PER_SLOT + id * PORTS_PER_NODE ;
106+ let base_port = BASE_PORT + port_offset;
107+
108+ if base_port > 60000 {
109+ panic ! ( "Calculated port {base_port} is too high. Reduce concurrency or port spacing." ) ;
110+ }
80111
81112 let ( validators, private_keys) = make_validators ( nodes, & params) ;
82113 let validator_set = ValidatorSet :: new ( validators) ;
0 commit comments