Skip to content

Commit

Permalink
[testing] serial testing perserving terminal (#791)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nthparameter committed Aug 5, 2023
1 parent 55739aa commit 00f7d06
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 4 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -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
Expand Down
32 changes: 32 additions & 0 deletions src/cursor/sys/windows.rs
Expand Up @@ -207,13 +207,18 @@ impl From<Handle> for ScreenBufferCursor {

Check failure on line 207 in src/cursor/sys/windows.rs

View workflow job for this annotation

GitHub Actions / stable on ubuntu-latest

Diff in /home/runner/work/crossterm/crossterm/src/cursor/sys/windows.rs
#[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,
};

Check failure on line 214 in src/cursor/sys/windows.rs

View workflow job for this annotation

GitHub Actions / stable on ubuntu-latest

Diff in /home/runner/work/crossterm/crossterm/src/cursor/sys/windows.rs
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();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand Down
1 change: 1 addition & 0 deletions src/style/types/color.rs
Expand Up @@ -380,6 +380,7 @@ mod tests {
}

Check failure on line 380 in src/style/types/color.rs

View workflow job for this annotation

GitHub Actions / stable on ubuntu-latest

Diff in /home/runner/work/crossterm/crossterm/src/style/types/color.rs
}


#[cfg(test)]
#[cfg(feature = "serde")]
mod serde_tests {
Expand Down
4 changes: 4 additions & 0 deletions src/terminal/sys.rs
Expand Up @@ -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;
Expand Down
44 changes: 40 additions & 4 deletions src/terminal/sys/windows.rs
Expand Up @@ -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();
Expand Down Expand Up @@ -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();

Expand Down

0 comments on commit 00f7d06

Please sign in to comment.