Code
This commit is contained in:
299
tests/ahfail_tests.rs
Normal file
299
tests/ahfail_tests.rs
Normal file
@@ -0,0 +1,299 @@
|
||||
use ahfail_module::*;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{glib, gdk_pixbuf, gdk};
|
||||
use gstreamer as gst;
|
||||
use std::ptr;
|
||||
use std::ffi::c_void;
|
||||
use gtk::glib::translate::{ToGlibPtr, Stash};
|
||||
|
||||
// Mock implementation of the resource getter for tests
|
||||
#[no_mangle]
|
||||
extern "C" fn ahfail_get_resource() -> *mut gtk::gio::ffi::GResource {
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
// Helper struct to simulate the C flexible array member allocation
|
||||
#[repr(C)]
|
||||
struct WindowWithStorage {
|
||||
window: Window,
|
||||
// Reserve space for the 'module_data' flexible array (1 pointer)
|
||||
storage: *mut c_void,
|
||||
}
|
||||
|
||||
fn setup_test_environment() {
|
||||
static ONCE: std::sync::Once = std::sync::Once::new();
|
||||
ONCE.call_once(|| {
|
||||
let _ = gtk::init();
|
||||
let _ = gst::init();
|
||||
});
|
||||
}
|
||||
|
||||
fn flush_events() {
|
||||
while gtk::events_pending() {
|
||||
gtk::main_iteration();
|
||||
}
|
||||
}
|
||||
|
||||
fn create_mock_context() -> (WindowWithStorage, gtk::Window, gtk::Label, gtk::Overlay, gdk::Display) {
|
||||
setup_test_environment();
|
||||
|
||||
let error_label = gtk::Label::new(Some(""));
|
||||
let overlay = gtk::Overlay::new();
|
||||
let window_widget = gtk::Window::new(gtk::WindowType::Toplevel);
|
||||
let display = gdk::Display::default().expect("No display available for testing");
|
||||
let monitor = display.monitor(0).expect("No monitor available");
|
||||
|
||||
// Explicitly type the Stash to avoid ambiguity
|
||||
let monitor_stash: Stash<*mut gdk::ffi::GdkMonitor, _> = monitor.to_glib_none();
|
||||
let window_stash: Stash<*mut gtk::ffi::GtkWindow, _> = window_widget.to_glib_none();
|
||||
let overlay_stash: Stash<*mut gtk::ffi::GtkOverlay, _> = overlay.to_glib_none();
|
||||
let label_stash: Stash<*mut gtk::ffi::GtkLabel, _> = error_label.to_glib_none();
|
||||
|
||||
let win = Window {
|
||||
monitor: monitor_stash.0,
|
||||
window: window_stash.0 as *mut gtk::ffi::GtkWidget,
|
||||
overlay: overlay_stash.0,
|
||||
window_box: ptr::null_mut(),
|
||||
body_revealer: ptr::null_mut(),
|
||||
body_grid: ptr::null_mut(),
|
||||
input_label: ptr::null_mut(),
|
||||
input_field: ptr::null_mut(),
|
||||
message_revealer: ptr::null_mut(),
|
||||
message_scrolled_window: ptr::null_mut(),
|
||||
message_box: ptr::null_mut(),
|
||||
unlock_button: ptr::null_mut(),
|
||||
error_label: label_stash.0 as *mut gtk::ffi::GtkWidget,
|
||||
warning_label: ptr::null_mut(),
|
||||
info_box: ptr::null_mut(),
|
||||
time_box: ptr::null_mut(),
|
||||
clock_label: ptr::null_mut(),
|
||||
date_label: ptr::null_mut(),
|
||||
module_data: __IncompleteArrayField::new(),
|
||||
};
|
||||
|
||||
let storage = WindowWithStorage {
|
||||
window: win,
|
||||
storage: ptr::null_mut(),
|
||||
};
|
||||
|
||||
(storage, window_widget, error_label, overlay, display)
|
||||
}
|
||||
|
||||
fn inject_test_state() {
|
||||
let pixbuf = gdk_pixbuf::Pixbuf::new(gdk_pixbuf::Colorspace::Rgb, false, 8, 1, 1).unwrap();
|
||||
let anim = gdk_pixbuf::PixbufSimpleAnim::new(1, 1, 1.0);
|
||||
anim.add_frame(&pixbuf);
|
||||
|
||||
// Use a minimal valid WAV (silent) to avoid GStreamer errors
|
||||
let wav_base64 = "UklGRiQAAABXQVZFZm10IBAAAAABAAEAQB8AAIA+AAACABAAZGF0YQEAAAAA";
|
||||
let data_uri = format!("data:audio/wav;base64,{}", wav_base64);
|
||||
|
||||
MODULE_STATE.with(|state| {
|
||||
let mut state = state.borrow_mut();
|
||||
state.animation = Some(anim.upcast());
|
||||
state.audio_uri = Some(data_uri);
|
||||
});
|
||||
}
|
||||
|
||||
fn run_test_01_initialization() {
|
||||
unsafe {
|
||||
on_activation(ptr::null_mut(), 0);
|
||||
}
|
||||
MODULE_STATE.with(|state| {
|
||||
let state = state.borrow();
|
||||
// It should have set the audio URI to the default
|
||||
assert_eq!(state.audio_uri.as_deref(), Some("resource:///ahfail/audio/magic-word.mp3"));
|
||||
});
|
||||
}
|
||||
|
||||
fn run_test_02_window_create_null() {
|
||||
unsafe {
|
||||
on_window_create(ptr::null_mut(), ptr::null_mut());
|
||||
}
|
||||
}
|
||||
|
||||
fn run_test_03_window_create_valid() {
|
||||
let (mut win_storage, _w, _l, _o, _) = create_mock_context();
|
||||
let ctx_ptr = &mut win_storage.window as *mut Window;
|
||||
|
||||
unsafe {
|
||||
on_window_create(ptr::null_mut(), ctx_ptr);
|
||||
let data = get_window_data_ref(ctx_ptr);
|
||||
assert!(data.is_some(), "WindowData should be initialized");
|
||||
|
||||
on_window_destroy(ptr::null_mut(), ctx_ptr);
|
||||
flush_events();
|
||||
}
|
||||
}
|
||||
|
||||
fn run_test_04_trigger_effect_sprite() {
|
||||
let (mut win_storage, _w, label, _o, _) = create_mock_context();
|
||||
let ctx_ptr = &mut win_storage.window as *mut Window;
|
||||
inject_test_state();
|
||||
|
||||
unsafe {
|
||||
on_window_create(ptr::null_mut(), ctx_ptr);
|
||||
label.set_text("Error");
|
||||
flush_events();
|
||||
|
||||
let data = get_window_data_ref(ctx_ptr).unwrap();
|
||||
assert_eq!(data.sprites.len(), 1);
|
||||
|
||||
on_window_destroy(ptr::null_mut(), ctx_ptr);
|
||||
flush_events();
|
||||
}
|
||||
}
|
||||
|
||||
fn run_test_05_trigger_effect_audio() {
|
||||
let (mut win_storage, _w, label, _o, _) = create_mock_context();
|
||||
let ctx_ptr = &mut win_storage.window as *mut Window;
|
||||
inject_test_state();
|
||||
|
||||
unsafe {
|
||||
on_window_create(ptr::null_mut(), ctx_ptr);
|
||||
label.set_text("Error");
|
||||
flush_events();
|
||||
|
||||
let data = get_window_data_ref(ctx_ptr).unwrap();
|
||||
assert_eq!(data.active_players.len(), 1);
|
||||
|
||||
on_window_destroy(ptr::null_mut(), ctx_ptr);
|
||||
flush_events();
|
||||
}
|
||||
}
|
||||
|
||||
fn run_test_06_multiple_triggers() {
|
||||
let (mut win_storage, _w, label, _o, _) = create_mock_context();
|
||||
let ctx_ptr = &mut win_storage.window as *mut Window;
|
||||
inject_test_state();
|
||||
|
||||
unsafe {
|
||||
on_window_create(ptr::null_mut(), ctx_ptr);
|
||||
for _ in 0..5 {
|
||||
label.set_text("Error");
|
||||
flush_events();
|
||||
}
|
||||
|
||||
let data = get_window_data_ref(ctx_ptr).unwrap();
|
||||
assert_eq!(data.sprites.len(), 5);
|
||||
assert_eq!(data.active_players.len(), 5);
|
||||
|
||||
on_window_destroy(ptr::null_mut(), ctx_ptr);
|
||||
flush_events();
|
||||
}
|
||||
}
|
||||
|
||||
fn run_test_07_window_cleanup() {
|
||||
let (mut win_storage, _w, label, _o, _) = create_mock_context();
|
||||
let ctx_ptr = &mut win_storage.window as *mut Window;
|
||||
inject_test_state();
|
||||
|
||||
unsafe {
|
||||
on_window_create(ptr::null_mut(), ctx_ptr);
|
||||
label.set_text("Error");
|
||||
flush_events();
|
||||
|
||||
assert!(get_window_data_ref(ctx_ptr).is_some());
|
||||
on_window_destroy(ptr::null_mut(), ctx_ptr);
|
||||
assert!(get_window_data_ref(ctx_ptr).is_none());
|
||||
flush_events();
|
||||
}
|
||||
}
|
||||
|
||||
fn run_test_08_multiple_windows() {
|
||||
let (mut win1, _w1, label1, _o1, _) = create_mock_context();
|
||||
let (mut win2, _w2, label2, _o2, _) = create_mock_context();
|
||||
let ctx1 = &mut win1.window as *mut Window;
|
||||
let ctx2 = &mut win2.window as *mut Window;
|
||||
inject_test_state();
|
||||
|
||||
unsafe {
|
||||
on_window_create(ptr::null_mut(), ctx1);
|
||||
on_window_create(ptr::null_mut(), ctx2);
|
||||
|
||||
label1.set_text("E1");
|
||||
flush_events();
|
||||
|
||||
label2.set_text("E2");
|
||||
label2.set_text("E2 again");
|
||||
flush_events();
|
||||
|
||||
let data1 = get_window_data_ref(ctx1).unwrap();
|
||||
let data2 = get_window_data_ref(ctx2).unwrap();
|
||||
|
||||
assert_eq!(data1.sprites.len(), 1);
|
||||
assert_eq!(data2.sprites.len(), 2);
|
||||
|
||||
on_window_destroy(ptr::null_mut(), ctx1);
|
||||
on_window_destroy(ptr::null_mut(), ctx2);
|
||||
flush_events();
|
||||
}
|
||||
}
|
||||
|
||||
fn run_test_09_idle_hide_cleanup() {
|
||||
let (mut win_storage, _w, label, _o, _) = create_mock_context();
|
||||
let ctx_ptr = &mut win_storage.window as *mut Window;
|
||||
inject_test_state();
|
||||
|
||||
unsafe {
|
||||
on_window_create(ptr::null_mut(), ctx_ptr);
|
||||
label.set_text("Error");
|
||||
flush_events();
|
||||
|
||||
// Mock GtkLock struct
|
||||
let mut windows_array = glib::ffi::g_array_new(0, 0, std::mem::size_of::<*mut Window>() as u32);
|
||||
glib::ffi::g_array_append_vals(windows_array, &ctx_ptr as *const _ as *const c_void, 1);
|
||||
|
||||
let mut lock = GtkLock {
|
||||
windows: windows_array,
|
||||
};
|
||||
|
||||
on_idle_hide(&mut lock);
|
||||
|
||||
assert!(get_window_data_ref(ctx_ptr).is_none());
|
||||
glib::ffi::g_array_free(windows_array, 1);
|
||||
flush_events();
|
||||
}
|
||||
}
|
||||
|
||||
fn run_test_10_module_unload() {
|
||||
inject_test_state();
|
||||
MODULE_STATE.with(|state| {
|
||||
assert!(state.borrow().audio_uri.is_some());
|
||||
});
|
||||
unsafe {
|
||||
g_module_unload(ptr::null_mut());
|
||||
}
|
||||
MODULE_STATE.with(|state| {
|
||||
let state = state.borrow();
|
||||
assert!(state.animation.is_none());
|
||||
assert!(state.audio_uri.is_none());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_all_tests_sequentially() {
|
||||
println!("Running test 01...");
|
||||
run_test_01_initialization();
|
||||
println!("Running test 02...");
|
||||
run_test_02_window_create_null();
|
||||
println!("Running test 03...");
|
||||
run_test_03_window_create_valid();
|
||||
println!("Running test 04...");
|
||||
run_test_04_trigger_effect_sprite();
|
||||
println!("Running test 05...");
|
||||
run_test_05_trigger_effect_audio();
|
||||
println!("Running test 06...");
|
||||
run_test_06_multiple_triggers();
|
||||
println!("Running test 07...");
|
||||
run_test_07_window_cleanup();
|
||||
println!("Running test 08...");
|
||||
run_test_08_multiple_windows();
|
||||
println!("Running test 09...");
|
||||
run_test_09_idle_hide_cleanup();
|
||||
println!("Running test 10...");
|
||||
run_test_10_module_unload();
|
||||
|
||||
// Final flush
|
||||
flush_events();
|
||||
}
|
||||
57
tests/benchmarks.rs
Normal file
57
tests/benchmarks.rs
Normal file
@@ -0,0 +1,57 @@
|
||||
use gtk::prelude::*;
|
||||
use gtk::{gdk, gdk_pixbuf};
|
||||
use gstreamer as gst;
|
||||
use gstreamer_player as gst_player;
|
||||
use std::time::Instant;
|
||||
|
||||
#[cfg(test)]
|
||||
mod benchmarks {
|
||||
use super::*;
|
||||
use std::sync::Once;
|
||||
|
||||
static INIT: Once = Once::new();
|
||||
|
||||
fn init() {
|
||||
INIT.call_once(|| {
|
||||
gtk::init().unwrap();
|
||||
gst::init().unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bench_pixbuf_loading() {
|
||||
init();
|
||||
let start = Instant::now();
|
||||
// Create a 1x1 pixbuf to simulate loading
|
||||
let _pixbuf = gdk_pixbuf::Pixbuf::new(gdk_pixbuf::Colorspace::Rgb, false, 8, 220, 220).unwrap();
|
||||
println!("Pixbuf creation: {:?}", start.elapsed());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bench_animation_creation() {
|
||||
init();
|
||||
let start = Instant::now();
|
||||
let _anim = gdk_pixbuf::PixbufSimpleAnim::new(220, 220, 12.0);
|
||||
println!("Animation creation: {:?}", start.elapsed());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bench_player_creation() {
|
||||
init();
|
||||
let start = Instant::now();
|
||||
let _player = gst_player::Player::new(None, None);
|
||||
println!("GstPlayer creation: {:?}", start.elapsed());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bench_coord_calculation() {
|
||||
use rand::Rng;
|
||||
let start = Instant::now();
|
||||
let mut rng = rand::thread_rng();
|
||||
for _ in 0..1000 {
|
||||
let _x = rng.gen_range(0..1920);
|
||||
let _y = rng.gen_range(0..1080);
|
||||
}
|
||||
println!("1000 RNG calculations: {:?}", start.elapsed());
|
||||
}
|
||||
}
|
||||
18
tests/module_test.c
Normal file
18
tests/module_test.c
Normal file
@@ -0,0 +1,18 @@
|
||||
#include <stddef.h>
|
||||
|
||||
#include "ahfail/module.h"
|
||||
|
||||
int main(void) {
|
||||
if (module_name[0] == '\0') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
on_activation(NULL, 42);
|
||||
on_window_create(NULL, NULL);
|
||||
on_focus_change(NULL, NULL, NULL);
|
||||
on_idle_show(NULL);
|
||||
on_idle_hide(NULL);
|
||||
on_window_destroy(NULL, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user