@@ -13,10 +13,11 @@ use crate::{
1313use iddqd:: { IdHashItem , IdHashMap , id_upcast} ;
1414use indexmap:: IndexSet ;
1515use indicatif:: { MultiProgress , ProgressBar , ProgressDrawTarget , ProgressStyle } ;
16+ use itertools:: Itertools as _;
1617use nextest_metadata:: RustBinaryId ;
1718use owo_colors:: OwoColorize ;
1819use std:: {
19- collections :: VecDeque ,
20+ cmp :: max ,
2021 env, fmt,
2122 io:: { self , IsTerminal , Write } ,
2223 num:: NonZero ,
@@ -152,19 +153,16 @@ impl SummaryBar {
152153
153154#[ derive( Debug ) ]
154155pub ( super ) struct TestProgressBars {
155- // A list of currently unused progress bars.
156- //
157- // Once a test bar is removed, we add an empty bar, then use it to avoid the
158- // overall progress bar shifting rapidly up and down.
159- free_bars : VecDeque < ProgressBar > ,
160156 used_bars : IdHashMap < TestProgressBar > ,
161157
162158 // Overflow tests in FIFO order (the IndexSet preserves insertion order).
163159 //
164160 // Tests are displayed if they're in used_bars but not in overflow_order.
165161 overflow_queue : IndexSet < ( RustBinaryId , String ) > ,
166162 // Maximum number of tests to display.
167- max_displayed : MaxProgressRunning ,
163+ max_progress_running : MaxProgressRunning ,
164+ // Actual maximum number of test displayed
165+ max_displayed : usize ,
168166
169167 // Summary bar showing the overflow count. This is Some if the summary bar
170168 // is currently displayed.
@@ -174,12 +172,12 @@ pub(super) struct TestProgressBars {
174172}
175173
176174impl TestProgressBars {
177- fn new ( spinner_chars : & ' static str , max_displayed : MaxProgressRunning ) -> Self {
175+ fn new ( spinner_chars : & ' static str , max_progress_running : MaxProgressRunning ) -> Self {
178176 Self {
179- free_bars : VecDeque :: new ( ) ,
180177 used_bars : IdHashMap :: new ( ) ,
181178 overflow_queue : IndexSet :: new ( ) ,
182- max_displayed,
179+ max_progress_running,
180+ max_displayed : 0 ,
183181 summary_bar : None ,
184182 spinner_chars,
185183 }
@@ -231,22 +229,7 @@ impl TestProgressBars {
231229 // multi-progress.
232230 multi. remove ( & data. bar ) ;
233231
234- if self . promote_next_overflow_test ( multi) {
235- // A test was promoted from the overflow queue. The promoted
236- // test takes this bar's place, so we don't need to add an
237- // empty bar for spacing.
238- } else {
239- // No test was promoted: add an empty bar for spacing.
240- let free_bar = ProgressBar :: hidden ( ) ;
241- free_bar. set_style (
242- ProgressStyle :: default_spinner ( )
243- . template ( " " )
244- . expect ( "this template is valid" ) ,
245- ) ;
246- let free_bar = multi. add ( free_bar) ;
247- free_bar. tick ( ) ;
248- self . free_bars . push_back ( free_bar) ;
249- }
232+ self . promote_next_overflow_test ( multi) ;
250233 }
251234
252235 // Update summary bar to reflect the new overflow count.
@@ -295,17 +278,12 @@ impl TestProgressBars {
295278 . len ( )
296279 . saturating_sub ( self . overflow_queue . len ( ) ) ;
297280
298- let should_display = match self . max_displayed {
281+ let should_display = match self . max_progress_running {
299282 MaxProgressRunning :: Infinite => true ,
300283 MaxProgressRunning :: Count ( max) => displayed_count < max. get ( ) ,
301284 } ;
302285
303286 let bar = if should_display {
304- // Remove a free bar if available.
305- if let Some ( free_bar) = self . free_bars . pop_front ( ) {
306- multi. remove ( & free_bar) ;
307- }
308-
309287 // Insert the bar at the correct position.
310288 //
311289 // displayed_count doesn't include this test yet, so insert at
@@ -332,14 +310,16 @@ impl TestProgressBars {
332310
333311 // Update the summary bar to reflect the overflow count.
334312 self . update_summary_bar ( multi, running_count, styles) ;
313+
314+ self . max_displayed = max ( self . max_displayed , self . len ( ) ) ;
335315 }
336316
337317 /// Updates or creates/removes the summary bar showing the overflow count.
338318 fn update_summary_bar ( & mut self , multi : & MultiProgress , running_count : usize , styles : & Styles ) {
339319 // Use the running count rather than the length of the overflow queue.
340320 // Since tests are added to the queue with a bit of a delay, using the
341321 // running count reduces flickering.
342- let overflow_count = match self . max_displayed {
322+ let overflow_count = match self . max_progress_running {
343323 MaxProgressRunning :: Count ( count) => running_count. saturating_sub ( count. get ( ) ) ,
344324 MaxProgressRunning :: Infinite => {
345325 // No summary bar is displayed in this case.
@@ -352,13 +332,6 @@ impl TestProgressBars {
352332 bar. set_overflow_count ( overflow_count, styles) ;
353333 } else {
354334 // Add a summary bar.
355- //
356- // Remove a free bar if available (the summary bar takes its
357- // place).
358- if let Some ( free_bar) = self . free_bars . pop_front ( ) {
359- multi. remove ( & free_bar) ;
360- }
361-
362335 let displayed_count = self
363336 . used_bars
364337 . len ( )
@@ -375,16 +348,6 @@ impl TestProgressBars {
375348 // The above Option::take removes the summary bar from
376349 // self.summary_bar when the overflow count reaches 0.
377350 multi. remove ( & summary_bar. bar ) ;
378- // Add a free bar for spacing.
379- let free_bar = ProgressBar :: hidden ( ) ;
380- free_bar. set_style (
381- ProgressStyle :: default_spinner ( )
382- . template ( " " )
383- . expect ( "this template is valid" ) ,
384- ) ;
385- let free_bar = multi. add ( free_bar) ;
386- free_bar. tick ( ) ;
387- self . free_bars . push_back ( free_bar) ;
388351 }
389352 }
390353
@@ -403,12 +366,6 @@ impl TestProgressBars {
403366 . find ( |tb| tb. binary_id == test_key. 0 && tb. test_name == test_key. 1 ) ;
404367
405368 if let Some ( tb) = tb {
406- // Remove a free bar if available (we're taking up a display
407- // slot).
408- if let Some ( free_bar) = self . free_bars . pop_front ( ) {
409- multi. remove ( & free_bar) ;
410- }
411-
412369 // Make the bar visible by adding it to the multi-progress.
413370 //
414371 // We've already removed this test from overflow_order, so it's
@@ -443,13 +400,28 @@ impl TestProgressBars {
443400 for tb in & self . used_bars {
444401 tb. bar . finish_and_clear ( ) ;
445402 }
446- for bar in & self . free_bars {
447- bar. finish_and_clear ( ) ;
448- }
449403 if let Some ( summary_bar) = & self . summary_bar {
450404 summary_bar. bar . finish_and_clear ( ) ;
451405 }
452406 }
407+
408+ fn len ( & self ) -> usize {
409+ self . used_bars . len ( ) + self . summary_bar . is_some ( ) as usize
410+ }
411+
412+ fn new_free_bar ( & self , multi_progress : & MultiProgress ) -> ProgressBar {
413+ // push an bar with as much line as needed to match the max total lines displayed
414+ let missing_lines = self . max_displayed - self . len ( ) ;
415+ let bar = ProgressBar :: hidden ( ) ;
416+ bar. set_style (
417+ ProgressStyle :: default_spinner ( )
418+ . template ( & std:: iter:: repeat_n ( " " , missing_lines) . join ( "\n " ) )
419+ . expect ( "this template is valid" ) ,
420+ ) ;
421+ let bar = multi_progress. add ( bar) ;
422+ bar. tick ( ) ;
423+ bar
424+ }
453425}
454426
455427#[ derive( Debug ) ]
@@ -741,8 +713,17 @@ impl ProgressBarState {
741713 // suspend forces a full redraw, so we call it only if there is
742714 // something in the buffer
743715 if !buf. is_empty ( ) {
744- self . multi_progress
745- . suspend ( || std:: io:: stderr ( ) . write_all ( buf) )
716+ let mut free_bar = None ;
717+ if let Some ( test_bars) = & self . test_bars {
718+ free_bar = Some ( test_bars. new_free_bar ( & self . multi_progress ) ) ;
719+ }
720+ let res = self
721+ . multi_progress
722+ . suspend ( || std:: io:: stderr ( ) . write_all ( buf) ) ;
723+ if let Some ( bar) = & free_bar {
724+ self . multi_progress . remove ( bar) ;
725+ }
726+ res
746727 } else {
747728 Ok ( ( ) )
748729 }
0 commit comments