[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.
This commit is contained in:
Dave 2023-08-05 06:36:42 -07:00 committed by GitHub
parent 55739aa786
commit 00f7d06151
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 78 additions and 4 deletions

View File

@ -76,6 +76,7 @@ futures = "0.3"
futures-timer = "3.0"
async-std = "1.12"
serde_json = "1.0"
serial_test = "2.0.0"
#
# Examples

View File

@ -207,13 +207,18 @@ impl From<Handle> for ScreenBufferCursor {
#[cfg(test)]
mod tests {
use serial_test::serial;
use super::{
move_down, move_left, move_right, move_to, move_to_column, move_to_next_line,
move_to_previous_line, move_to_row, move_up, position, restore_position, save_position,
};
use crate::terminal::sys::temp_screen_buffer;
#[test]
#[serial]
fn test_move_to_winapi() {
let _test_screen = temp_screen_buffer().unwrap();
let (saved_x, saved_y) = position().unwrap();
move_to(saved_x + 1, saved_y + 1).unwrap();
@ -224,14 +229,20 @@ mod tests {
}
#[test]
#[serial]
fn test_move_right_winapi() {
let _test_screen = temp_screen_buffer().unwrap();
let (saved_x, saved_y) = position().unwrap();
move_right(1).unwrap();
assert_eq!(position().unwrap(), (saved_x + 1, saved_y));
}
#[test]
#[serial]
fn test_move_left_winapi() {
let _test_screen = temp_screen_buffer().unwrap();
move_to(2, 0).unwrap();
move_left(2).unwrap();
@ -240,7 +251,10 @@ mod tests {
}
#[test]
#[serial]
fn test_move_up_winapi() {
let _test_screen = temp_screen_buffer().unwrap();
move_to(0, 2).unwrap();
move_up(2).unwrap();
@ -249,7 +263,10 @@ mod tests {
}
#[test]
#[serial]
fn test_move_to_next_line_winapi() {
let _test_screen = temp_screen_buffer().unwrap();
move_to(0, 2).unwrap();
move_to_next_line(2).unwrap();
@ -258,7 +275,10 @@ mod tests {
}
#[test]
#[serial]
fn test_move_to_previous_line_winapi() {
let _test_screen = temp_screen_buffer().unwrap();
move_to(0, 2).unwrap();
move_to_previous_line(2).unwrap();
@ -267,7 +287,10 @@ mod tests {
}
#[test]
#[serial]
fn test_move_to_column_winapi() {
let _test_screen = temp_screen_buffer().unwrap();
move_to(0, 2).unwrap();
move_to_column(12).unwrap();
@ -276,7 +299,10 @@ mod tests {
}
#[test]
#[serial]
fn test_move_to_row_winapi() {
let _test_screen = temp_screen_buffer().unwrap();
move_to(0, 2).unwrap();
move_to_row(5).unwrap();
@ -285,7 +311,10 @@ mod tests {
}
#[test]
#[serial]
fn test_move_down_winapi() {
let _test_screen = temp_screen_buffer().unwrap();
move_to(0, 0).unwrap();
move_down(2).unwrap();
@ -294,7 +323,10 @@ mod tests {
}
#[test]
#[serial]
fn test_save_restore_position_winapi() {
let _test_screen = temp_screen_buffer().unwrap();
let (saved_x, saved_y) = position().unwrap();
save_position().unwrap();

View File

@ -380,6 +380,7 @@ mod tests {
}
}
#[cfg(test)]
#[cfg(feature = "serde")]
mod serde_tests {

View File

@ -15,6 +15,10 @@ pub(crate) use self::windows::{
clear, disable_raw_mode, enable_raw_mode, window_size, is_raw_mode_enabled, scroll_down,
scroll_up, set_size, set_window_title, size,
};
#[cfg(all(windows, test))]
pub(crate) use self::windows::{
temp_screen_buffer,
};
#[cfg(windows)]
mod windows;

View File

@ -351,21 +351,54 @@ fn clear_winapi(
Ok(())
}
#[cfg(test)]
// Create a new screen buffer to avoid changing the terminal the test
// is running within.
pub fn temp_screen_buffer() -> std::io::Result<ScreenBuffer> {
let alternate_screen = ScreenBuffer::create()?;
alternate_screen.show().unwrap();
Ok(alternate_screen)
}
#[cfg(test)]
mod tests {
use std::{ffi::OsString, os::windows::ffi::OsStringExt};
use crossterm_winapi::ScreenBuffer;
use serial_test::serial;
use winapi::um::wincon::GetConsoleTitleW;
use super::{scroll_down, scroll_up, set_size, set_window_title, size};
use super::{scroll_down, scroll_up, set_size, set_window_title, size, temp_screen_buffer};
#[test]
fn test_resize_winapi() {
#[serial]
fn test_resize_winapi_20_21() {
let _test_screen = temp_screen_buffer().unwrap();
let (width, height) = size().unwrap();
set_size(30, 30).unwrap();
assert_eq!((30, 30), size().unwrap());
// The values 20 and 21 are arbitrary and different from each other
// just to see they're not crossed over.
set_size(20, 21).unwrap();
assert_eq!((20, 21), size().unwrap());
// reset to previous size
set_size(width, height).unwrap();
assert_eq!((width, height), size().unwrap());
}
// This is similar to test_resize_winapi_20_21() above. This verifies that
// another test of similar functionality runs independently (that a testing
// race condition has been addressed).
#[test]
#[serial]
fn test_resize_winapi_30_31() {
let _test_screen = temp_screen_buffer().unwrap();
let (width, height) = size().unwrap();
set_size(30, 31).unwrap();
assert_eq!((30, 31), size().unwrap());
// reset to previous size
set_size(width, height).unwrap();
@ -420,7 +453,10 @@ mod tests {
}
#[test]
#[serial]
fn test_set_title_winapi() {
let _test_screen = temp_screen_buffer().unwrap();
let test_title = "this is a crossterm test title";
set_window_title(test_title).unwrap();