Skip to content

Commit 00f7d06

Browse files
authored
[testing] serial testing perserving terminal (#791)
This change does two things: - add the serial_test crate to run selected tests serial rather than in parallel. This is done because they use global state so running them in parallel leads to race conditions and flaky results (sometimes they pass, sometimes they fail). Running them serialy avoids this flakiness. - create a screen buffer within the test. This avoids changing the terminal (screen buffer) which is running the test. for example, a test that changes the terminal size to 20 x 20 can leave the developer running the test with a resized terminal. Creating a separate screen buffer for the test avoids this.
1 parent 55739aa commit 00f7d06

File tree

5 files changed

+78
-4
lines changed

5 files changed

+78
-4
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ futures = "0.3"
7676
futures-timer = "3.0"
7777
async-std = "1.12"
7878
serde_json = "1.0"
79+
serial_test = "2.0.0"
7980

8081
#
8182
# Examples

src/cursor/sys/windows.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,13 +207,18 @@ impl From<Handle> for ScreenBufferCursor {
207207

208208
#[cfg(test)]
209209
mod tests {
210+
use serial_test::serial;
210211
use super::{
211212
move_down, move_left, move_right, move_to, move_to_column, move_to_next_line,
212213
move_to_previous_line, move_to_row, move_up, position, restore_position, save_position,
213214
};
215+
use crate::terminal::sys::temp_screen_buffer;
214216

215217
#[test]
218+
#[serial]
216219
fn test_move_to_winapi() {
220+
let _test_screen = temp_screen_buffer().unwrap();
221+
217222
let (saved_x, saved_y) = position().unwrap();
218223

219224
move_to(saved_x + 1, saved_y + 1).unwrap();
@@ -224,14 +229,20 @@ mod tests {
224229
}
225230

226231
#[test]
232+
#[serial]
227233
fn test_move_right_winapi() {
234+
let _test_screen = temp_screen_buffer().unwrap();
235+
228236
let (saved_x, saved_y) = position().unwrap();
229237
move_right(1).unwrap();
230238
assert_eq!(position().unwrap(), (saved_x + 1, saved_y));
231239
}
232240

233241
#[test]
242+
#[serial]
234243
fn test_move_left_winapi() {
244+
let _test_screen = temp_screen_buffer().unwrap();
245+
235246
move_to(2, 0).unwrap();
236247

237248
move_left(2).unwrap();
@@ -240,7 +251,10 @@ mod tests {
240251
}
241252

242253
#[test]
254+
#[serial]
243255
fn test_move_up_winapi() {
256+
let _test_screen = temp_screen_buffer().unwrap();
257+
244258
move_to(0, 2).unwrap();
245259

246260
move_up(2).unwrap();
@@ -249,7 +263,10 @@ mod tests {
249263
}
250264

251265
#[test]
266+
#[serial]
252267
fn test_move_to_next_line_winapi() {
268+
let _test_screen = temp_screen_buffer().unwrap();
269+
253270
move_to(0, 2).unwrap();
254271

255272
move_to_next_line(2).unwrap();
@@ -258,7 +275,10 @@ mod tests {
258275
}
259276

260277
#[test]
278+
#[serial]
261279
fn test_move_to_previous_line_winapi() {
280+
let _test_screen = temp_screen_buffer().unwrap();
281+
262282
move_to(0, 2).unwrap();
263283

264284
move_to_previous_line(2).unwrap();
@@ -267,7 +287,10 @@ mod tests {
267287
}
268288

269289
#[test]
290+
#[serial]
270291
fn test_move_to_column_winapi() {
292+
let _test_screen = temp_screen_buffer().unwrap();
293+
271294
move_to(0, 2).unwrap();
272295

273296
move_to_column(12).unwrap();
@@ -276,7 +299,10 @@ mod tests {
276299
}
277300

278301
#[test]
302+
#[serial]
279303
fn test_move_to_row_winapi() {
304+
let _test_screen = temp_screen_buffer().unwrap();
305+
280306
move_to(0, 2).unwrap();
281307

282308
move_to_row(5).unwrap();
@@ -285,7 +311,10 @@ mod tests {
285311
}
286312

287313
#[test]
314+
#[serial]
288315
fn test_move_down_winapi() {
316+
let _test_screen = temp_screen_buffer().unwrap();
317+
289318
move_to(0, 0).unwrap();
290319

291320
move_down(2).unwrap();
@@ -294,7 +323,10 @@ mod tests {
294323
}
295324

296325
#[test]
326+
#[serial]
297327
fn test_save_restore_position_winapi() {
328+
let _test_screen = temp_screen_buffer().unwrap();
329+
298330
let (saved_x, saved_y) = position().unwrap();
299331

300332
save_position().unwrap();

src/style/types/color.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ mod tests {
380380
}
381381
}
382382

383+
383384
#[cfg(test)]
384385
#[cfg(feature = "serde")]
385386
mod serde_tests {

src/terminal/sys.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ pub(crate) use self::windows::{
1515
clear, disable_raw_mode, enable_raw_mode, window_size, is_raw_mode_enabled, scroll_down,
1616
scroll_up, set_size, set_window_title, size,
1717
};
18+
#[cfg(all(windows, test))]
19+
pub(crate) use self::windows::{
20+
temp_screen_buffer,
21+
};
1822

1923
#[cfg(windows)]
2024
mod windows;

src/terminal/sys/windows.rs

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -351,21 +351,54 @@ fn clear_winapi(
351351
Ok(())
352352
}
353353

354+
#[cfg(test)]
355+
// Create a new screen buffer to avoid changing the terminal the test
356+
// is running within.
357+
pub fn temp_screen_buffer() -> std::io::Result<ScreenBuffer> {
358+
let alternate_screen = ScreenBuffer::create()?;
359+
alternate_screen.show().unwrap();
360+
Ok(alternate_screen)
361+
}
362+
354363
#[cfg(test)]
355364
mod tests {
356365
use std::{ffi::OsString, os::windows::ffi::OsStringExt};
357366

358367
use crossterm_winapi::ScreenBuffer;
368+
use serial_test::serial;
359369
use winapi::um::wincon::GetConsoleTitleW;
360370

361-
use super::{scroll_down, scroll_up, set_size, set_window_title, size};
371+
use super::{scroll_down, scroll_up, set_size, set_window_title, size, temp_screen_buffer};
362372

363373
#[test]
364-
fn test_resize_winapi() {
374+
#[serial]
375+
fn test_resize_winapi_20_21() {
376+
let _test_screen = temp_screen_buffer().unwrap();
377+
365378
let (width, height) = size().unwrap();
366379

367-
set_size(30, 30).unwrap();
368-
assert_eq!((30, 30), size().unwrap());
380+
// The values 20 and 21 are arbitrary and different from each other
381+
// just to see they're not crossed over.
382+
set_size(20, 21).unwrap();
383+
assert_eq!((20, 21), size().unwrap());
384+
385+
// reset to previous size
386+
set_size(width, height).unwrap();
387+
assert_eq!((width, height), size().unwrap());
388+
}
389+
390+
// This is similar to test_resize_winapi_20_21() above. This verifies that
391+
// another test of similar functionality runs independently (that a testing
392+
// race condition has been addressed).
393+
#[test]
394+
#[serial]
395+
fn test_resize_winapi_30_31() {
396+
let _test_screen = temp_screen_buffer().unwrap();
397+
398+
let (width, height) = size().unwrap();
399+
400+
set_size(30, 31).unwrap();
401+
assert_eq!((30, 31), size().unwrap());
369402

370403
// reset to previous size
371404
set_size(width, height).unwrap();
@@ -420,7 +453,10 @@ mod tests {
420453
}
421454

422455
#[test]
456+
#[serial]
423457
fn test_set_title_winapi() {
458+
let _test_screen = temp_screen_buffer().unwrap();
459+
424460
let test_title = "this is a crossterm test title";
425461
set_window_title(test_title).unwrap();
426462

0 commit comments

Comments
 (0)