added "manual" integration tests for basic system/window verification

since i cannot think of any way to verify these tests in a automated
way, these are all set up currently as "ignored" tests which need to be
manually run (either directly via the IDE, e.g. CLion as I use, or via
the an ALTERNATIVE test runner via the CLI (such as nextest). these
cannot be run all together via `cargo test` (even if running with only
a single thread). `cargo test` can only be used to run them successfully
one test per invocation.
This commit is contained in:
Gered 2023-04-14 13:18:47 -04:00
parent 9a421f32a6
commit fbf6d1e716
6 changed files with 451 additions and 0 deletions

View file

@ -0,0 +1,94 @@
use crate::BACKGROUND_COLOR;
use ggdt::prelude::*;
fn display_raw_keyboard_state(system: &mut System<Standard>, x: i32, y: i32) {
let font_opts = FontRenderOpts::Color(0xffffffff);
let font = &system.res.font;
system.res.video.print_string("Raw Keyboard State", x, x, font_opts, font);
let mut idx = 0;
for yc in 0..16 {
for xc in 0..32 {
let state = if let Some(scancode) = num::FromPrimitive::from_u32(idx) {
system.res.keyboard.is_key_down(scancode)
} else {
false
};
system.res.video.print_char(
if state { '1' } else { '0' },
x + (xc * 8),
y + 10 + (yc * 8),
font_opts,
font,
);
idx += 1;
}
}
}
fn display_key_state(key: Scancode, system: &mut System<Standard>, x: i32, y: i32) {
system.res.video.print_string(
&format!(
"{:?} - {} {} {} {}",
key,
system.res.keyboard.is_key_down(key) as i32,
system.res.keyboard.is_key_up(key) as i32,
system.res.keyboard.is_key_pressed(key) as i32,
system.res.keyboard.is_key_released(key) as i32,
),
x,
y,
FontRenderOpts::Color(0xffffffff),
&system.res.font,
);
}
#[test]
#[ignore]
fn keyboard_state() {
let config = StandardConfig::variable_screen_size(640, 480).scale_factor(2);
let mut system = SystemBuilder::new()
.window_title("Keyboard State") //
.vsync(true)
.build(config)
.unwrap();
while !system.do_events().unwrap() {
if system.res.keyboard.is_key_pressed(Scancode::Escape) {
break;
}
system.res.video.clear(BACKGROUND_COLOR);
system.update().unwrap();
display_raw_keyboard_state(&mut system, 2, 2);
for (idx, key) in [
Scancode::A,
Scancode::LCtrl,
Scancode::RCtrl,
Scancode::LAlt,
Scancode::RAlt,
Scancode::LShift,
Scancode::RShift,
Scancode::Return,
Scancode::KpEnter,
Scancode::Up,
Scancode::Down,
Scancode::Left,
Scancode::Right,
]
.iter()
.enumerate()
{
display_key_state(*key, &mut system, 2, 160 + (idx as i32 * 10));
}
system.res.video.set_pixel(system.res.mouse.x(), system.res.mouse.y(), to_rgb32(255, 0, 255));
system.display().unwrap();
}
}

53
ggdt/tests/manual/main.rs Normal file
View file

@ -0,0 +1,53 @@
//! These are tests that need to be manually run and interacted with by the user in order to verify functionality.
//! This is specifically for integration-style testing which is testing things like windowing, system events, input
//! devices, etc that are more difficult to test in a purely automated way.
//!
//! TODO: Is there a better way to do this, rather than marking all of these tests as ignored? We don't want them to
//! run during any automatic CI step, etc, as those should always run without manual user intervention midway.
//!
//! HACK: It is not currenly possible to run all of these together via cargo CLI, using something like
//! `cargo test -- --test-threads=1 --ignored`. Even restricting to 1 thread like this will fail. This appears
//! to be due to a bug in rust-sdl2 where `sdl2::sdl::IS_MAIN_THREAD_DECLARED` is never reset to false after
//! `SDL_Quit` is finally called (which only can happen when all subsystems are dropped). Attempting to restart
//! SDL even after a supposedly clean shutdown will always result in the error
//! "Cannot initialize `Sdl` from more than once thread."
//! However, you can run them via an alternative test runner like nextest (https://nexte.st/).
//! e.g.`cargo nextest run -j 1 --run-ignored ignored-only`
mod keyboard;
mod mouse;
mod system_events;
mod system_resources_doslike;
mod system_resources_standard;
use ggdt::prelude::*;
const BACKGROUND_COLOR: u32 = 0xff2c3041;
fn draw_base_screen<BitmapType>(
dest: &mut BitmapType,
bg_color_1: BitmapType::PixelType,
bg_color_2: BitmapType::PixelType,
color: BitmapType::PixelType,
highlight_color: BitmapType::PixelType,
) where
BitmapType: GeneralBitmap,
{
for y in 0..dest.height() as i32 {
dest.horiz_line(0, dest.right() as i32, y, if y % 2 == 0 { bg_color_1 } else { bg_color_2 });
}
dest.horiz_line(0, 16, 0, color);
dest.horiz_line(0, 16, dest.bottom() as i32, color);
dest.horiz_line(dest.right() as i32 - 16, dest.right() as i32, 0, color);
dest.horiz_line(dest.right() as i32 - 16, dest.right() as i32, dest.bottom() as i32, color);
dest.vert_line(0, 0, 16, color);
dest.vert_line(dest.right() as i32, 0, 16, color);
dest.vert_line(0, dest.bottom() as i32 - 16, dest.bottom() as i32, color);
dest.vert_line(dest.right() as i32, dest.bottom() as i32 - 16, dest.bottom() as i32, color);
dest.set_pixel(0, 0, highlight_color);
dest.set_pixel(0, dest.bottom() as i32, highlight_color);
dest.set_pixel(dest.right() as i32, 0, highlight_color);
dest.set_pixel(dest.right() as i32, dest.bottom() as i32, highlight_color);
}

View file

@ -0,0 +1,98 @@
use crate::BACKGROUND_COLOR;
use ggdt::prelude::*;
fn display_mouse_state(system: &mut System<Standard>) {
let font = &system.res.font;
let font_opts = FontRenderOpts::Color(0xffffffff);
system.res.video.print_string(
&format!(
"X: {:4} (delta: {:4})\nY: {:4} (delta: {:4})",
system.res.mouse.x(),
system.res.mouse.x_delta(),
system.res.mouse.y(),
system.res.mouse.y_delta(),
),
10,
10,
font_opts,
font,
);
system.res.video.print_string("Buttons - down/up/pressed/released", 10, 40, font_opts, font);
use MouseButton::*;
for (idx, button) in [Left, Middle, Right, X1, X2].iter().enumerate() {
system.res.video.print_string(
&format!(
"{:?} - {} {} {} {}",
button,
system.res.mouse.is_button_down(*button) as i32,
system.res.mouse.is_button_up(*button) as i32,
system.res.mouse.is_button_pressed(*button) as i32,
system.res.mouse.is_button_released(*button) as i32,
),
10,
50 + (idx as i32 * 8),
font_opts,
font,
)
}
}
#[test]
#[ignore] // should be manually run
fn mouse_with_custom_cursor() {
let config = StandardConfig::variable_screen_size(320, 240).scale_factor(4);
let mut system = SystemBuilder::new()
.window_title("Mouse Input, using a Custom Bitmap Cursor")
.vsync(true)
.show_mouse(false)
.build(config)
.unwrap();
system.res.cursor.enable_cursor(true);
while !system.do_events().unwrap() {
if system.res.keyboard.is_key_pressed(Scancode::Escape) {
break;
}
system.res.video.clear(BACKGROUND_COLOR);
system.update().unwrap();
display_mouse_state(&mut system);
system.res.video.set_pixel(system.res.mouse.x(), system.res.mouse.y(), to_rgb32(255, 0, 255));
system.display().unwrap();
}
}
#[test]
#[ignore] // should be manually run
fn mouse_with_os_cursor() {
let config = StandardConfig::variable_screen_size(320, 240).scale_factor(4);
let mut system = SystemBuilder::new()
.window_title("Mouse Input, using the OS's Native Cursor")
.vsync(true)
.show_mouse(true)
.build(config)
.unwrap();
system.res.cursor.enable_cursor(false);
while !system.do_events().unwrap() {
if system.res.keyboard.is_key_pressed(Scancode::Escape) {
break;
}
system.res.video.clear(BACKGROUND_COLOR);
system.update().unwrap();
display_mouse_state(&mut system);
system.res.video.set_pixel(system.res.mouse.x(), system.res.mouse.y(), to_rgb32(255, 0, 255));
system.display().unwrap();
}
}

View file

@ -0,0 +1,50 @@
use crate::BACKGROUND_COLOR;
use ggdt::prelude::*;
use std::collections::VecDeque;
#[test]
#[ignore] // should be manually run
fn system_events_display() {
let config = StandardConfig::variable_screen_size(640, 480).scale_factor(2);
let mut system = SystemBuilder::new()
.window_title("Displaying all SystemEvents")
.vsync(true)
.show_mouse(true)
.build(config)
.unwrap();
let mut recent_events = VecDeque::new();
'mainloop: loop {
while recent_events.len() > (system.res.video.height() as usize / system.res.font.line_height() as usize) {
recent_events.pop_back();
}
system.res.update_event_state().unwrap();
for event in system.event_pump.poll_iter() {
system.res.handle_event(&event).unwrap();
recent_events.push_front(event.clone());
if event == SystemEvent::Quit {
break 'mainloop;
}
}
system.update().unwrap();
if system.res.keyboard.is_key_pressed(Scancode::Escape) {
break 'mainloop;
}
system.res.video.clear(BACKGROUND_COLOR);
for (idx, event) in recent_events.iter().enumerate() {
system.res.video.print_string(
&format!("{:?}", event),
2,
system.res.video.height() as i32 - 10 - (idx as i32 * 10),
FontRenderOpts::Color(to_rgb32(255, 255, 255)),
&system.res.font,
);
}
system.display().unwrap();
}
}

View file

@ -0,0 +1,75 @@
use crate::draw_base_screen;
use ggdt::prelude::*;
fn draw_state(system: &mut System<DosLike>) {
system.res.video.print_string(
&format!(
"{}x{} (DosLike)\n\n{:3}, {:3}",
system.res.video.width(),
system.res.video.height(),
system.res.mouse.x(),
system.res.mouse.y()
),
10,
10,
FontRenderOpts::Color(15),
&system.res.font,
);
}
fn simple_main_loop(mut system: System<DosLike>) {
while !system.do_events().unwrap() {
if system.res.keyboard.is_key_pressed(Scancode::Escape) {
break;
}
system.update().unwrap();
draw_base_screen(&mut system.res.video, 18, 19, 15, 12);
draw_state(&mut system);
system.display().unwrap();
}
}
#[test]
#[ignore]
fn fixed_screen_size_integer_scaling() {
let config = DosLikeConfig::fixed_screen_size(320, 240, true).scale_factor(3);
let mut system = SystemBuilder::new()
.window_title("Fixed Screen Size with Integer Scaling (DosLike)")
.vsync(true)
.show_mouse(false)
.build(config)
.unwrap();
system.res.cursor.enable_cursor(true);
simple_main_loop(system);
}
#[test]
#[ignore]
fn fixed_screen_size_variable_scaling() {
let config = DosLikeConfig::fixed_screen_size(320, 240, false).scale_factor(3);
let mut system = SystemBuilder::new()
.window_title("Fixed Screen Size with Variable Scaling (DosLike)")
.vsync(true)
.show_mouse(false)
.build(config)
.unwrap();
system.res.cursor.enable_cursor(true);
simple_main_loop(system);
}
#[test]
#[ignore]
fn variable_screen_size_fixed_scale_factor() {
let config = DosLikeConfig::variable_screen_size(320, 240).scale_factor(3);
let mut system = SystemBuilder::new()
.window_title("Variable Screen Size with Fixed Scale Factor (DosLike)")
.vsync(true)
.show_mouse(false)
.build(config)
.unwrap();
system.res.cursor.enable_cursor(true);
simple_main_loop(system);
}

View file

@ -0,0 +1,81 @@
use crate::draw_base_screen;
use ggdt::prelude::*;
fn draw_state(system: &mut System<Standard>) {
system.res.video.print_string(
&format!(
"{}x{} (Standard)\n\n{:3}, {:3}",
system.res.video.width(),
system.res.video.height(),
system.res.mouse.x(),
system.res.mouse.y()
),
10,
10,
FontRenderOpts::Color(COLOR_BRIGHT_WHITE),
&system.res.font,
);
}
fn simple_main_loop(mut system: System<Standard>) {
while !system.do_events().unwrap() {
if system.res.keyboard.is_key_pressed(Scancode::Escape) {
break;
}
system.update().unwrap();
draw_base_screen(
&mut system.res.video,
to_rgb32(32, 32, 32),
to_rgb32(44, 44, 44),
COLOR_BRIGHT_WHITE,
COLOR_BRIGHT_RED,
);
draw_state(&mut system);
system.display().unwrap();
}
}
#[test]
#[ignore]
fn fixed_screen_size_integer_scaling() {
let config = StandardConfig::fixed_screen_size(320, 240, true).scale_factor(3);
let mut system = SystemBuilder::new()
.window_title("Fixed Screen Size with Integer Scaling (Standard)")
.vsync(true)
.show_mouse(false)
.build(config)
.unwrap();
system.res.cursor.enable_cursor(true);
simple_main_loop(system);
}
#[test]
#[ignore]
fn fixed_screen_size_variable_scaling() {
let config = StandardConfig::fixed_screen_size(320, 240, false).scale_factor(3);
let mut system = SystemBuilder::new()
.window_title("Fixed Screen Size with Variable Scaling (Standard)")
.vsync(true)
.show_mouse(false)
.build(config)
.unwrap();
system.res.cursor.enable_cursor(true);
simple_main_loop(system);
}
#[test]
#[ignore]
fn variable_screen_size_fixed_scale_factor() {
let config = StandardConfig::variable_screen_size(320, 240).scale_factor(3);
let mut system = SystemBuilder::new()
.window_title("Variable Screen Size with Fixed Scale Factor (Standard)")
.vsync(true)
.show_mouse(false)
.build(config)
.unwrap();
system.res.cursor.enable_cursor(true);
simple_main_loop(system);
}