feat: better testing (#131)

* add tests

* update github actions to include testing step

* optimization
This commit is contained in:
Tarasov Aleksandr
2026-06-02 21:37:22 +03:00
committed by GitHub
parent 0476329798
commit e91465365d
9 changed files with 469 additions and 0 deletions
+79
View File
@@ -218,3 +218,82 @@ impl HotkeyConfig {
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gui_config_default() {
let config = GuiConfig::default();
assert_eq!(config.scale_factor, 1.0);
assert_eq!(config.left_panel_width, 280.0);
assert!(!config.save_volume);
assert_eq!(config.preferred_theme, PreferredTheme::System);
}
#[test]
fn test_hotkey_config_operations() {
let mut config = HotkeyConfig::default();
assert!(config.slots.is_empty());
let req = Request::ping();
config.set_slot("slot1".to_string(), req.clone());
assert_eq!(config.slots.len(), 1);
assert_eq!(config.slots[0].slot, "slot1");
assert_eq!(config.slots[0].action, req);
assert!(config.slots[0].key_chord.is_none());
// Test find_slot
let found = config.find_slot("slot1");
assert!(found.is_some());
assert_eq!(found.unwrap().slot, "slot1");
// Test set_key_chord
let updated = config.set_key_chord("slot1", Some("Ctrl+A".to_string()));
assert!(updated);
assert_eq!(config.slots[0].key_chord.as_deref(), Some("Ctrl+A"));
// Test set_key_chord for non-existent slot
let updated_non_existent = config.set_key_chord("slot2", Some("Ctrl+B".to_string()));
assert!(!updated_non_existent);
// Test find_slot_mut
let found_mut = config.find_slot_mut("slot1");
assert!(found_mut.is_some());
found_mut.unwrap().key_chord = Some("Ctrl+B".to_string());
assert_eq!(config.slots[0].key_chord.as_deref(), Some("Ctrl+B"));
// Test slots_for_chord
let slots = config.slots_for_chord("Ctrl+B");
assert_eq!(slots.len(), 1);
assert_eq!(slots[0].slot, "slot1");
let empty_slots = config.slots_for_chord("Ctrl+A");
assert!(empty_slots.is_empty());
// Test remove_slot
let removed = config.remove_slot("slot1");
assert!(removed);
assert!(config.slots.is_empty());
let removed_non_existent = config.remove_slot("slot1");
assert!(!removed_non_existent);
}
#[test]
fn test_hotkey_config_conflicts() {
let mut config = HotkeyConfig::default();
config.set_slot("slot1".to_string(), Request::ping());
config.set_slot("slot2".to_string(), Request::ping());
config.set_slot("slot3".to_string(), Request::ping());
config.set_key_chord("slot1", Some("Ctrl+A".to_string()));
config.set_key_chord("slot2", Some("Ctrl+A".to_string())); // Conflict with slot1
config.set_key_chord("slot3", Some("Ctrl+B".to_string()));
let conflicts = config.find_conflicts();
assert_eq!(conflicts.len(), 1);
assert!(conflicts.contains(&("slot1", "slot2")) || conflicts.contains(&("slot2", "slot1")));
}
}
+81
View File
@@ -72,3 +72,84 @@ impl AudioDevice {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_audio_device_new() {
let device = AudioDevice::new(
1,
Some("NickName"),
Some("Description"),
Some("Name"),
DeviceType::Input,
);
assert_eq!(device.id, 1);
assert_eq!(device.nick, "NickName");
assert_eq!(device.name, "Name");
assert_eq!(device.device_type, DeviceType::Input);
// Fallbacks for nick
let device_no_nick =
AudioDevice::new(2, None, Some("Desc"), Some("Name"), DeviceType::Output);
assert_eq!(device_no_nick.nick, "Desc");
let device_no_desc = AudioDevice::new(3, None, None, Some("Name"), DeviceType::Output);
assert_eq!(device_no_desc.nick, "Name");
}
#[test]
fn test_audio_device_add_port() {
let mut device = AudioDevice::new(1, None, None, Some("device-name"), DeviceType::Input);
let port_fl = Port {
node_id: 1,
port_id: 10,
name: "input_FL".to_string(),
};
let port_fr = Port {
node_id: 1,
port_id: 11,
name: "input_FR".to_string(),
};
device.add_port(port_fl.clone());
device.add_port(port_fr.clone());
assert_eq!(device.input_fl, Some(port_fl));
assert_eq!(device.input_fr, Some(port_fr));
// Test output ports
let port_out_fl = Port {
node_id: 1,
port_id: 12,
name: "output_FL".to_string(),
};
let port_out_fr = Port {
node_id: 1,
port_id: 13,
name: "capture_FR".to_string(),
};
device.add_port(port_out_fl.clone());
device.add_port(port_out_fr.clone());
assert_eq!(device.output_fl, Some(port_out_fl));
assert_eq!(device.output_fr, Some(port_out_fr));
// Test MONO ports
let mut device_mono =
AudioDevice::new(2, None, None, Some("mono-device"), DeviceType::Input);
let port_mono = Port {
node_id: 2,
port_id: 20,
name: "input_MONO".to_string(),
};
device_mono.add_port(port_mono.clone());
assert_eq!(device_mono.input_fl, Some(port_mono.clone()));
assert_eq!(device_mono.input_fr, Some(port_mono));
}
}
+85
View File
@@ -238,3 +238,88 @@ impl Response {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_response_new() {
let res = Response::new(true, "success-msg");
assert!(res.status);
assert_eq!(res.message, "success-msg");
}
#[test]
fn test_request_constructors() {
// test ping
let req_ping = Request::ping();
assert_eq!(req_ping.name, "ping");
assert!(req_ping.args.is_empty());
// test kill
let req_kill = Request::kill();
assert_eq!(req_kill.name, "kill");
// test pause (with and without id)
let req_pause_no_id = Request::pause(None);
assert_eq!(req_pause_no_id.name, "pause");
assert!(req_pause_no_id.args.is_empty());
let req_pause_with_id = Request::pause(Some(42));
assert_eq!(req_pause_with_id.name, "pause");
assert_eq!(
req_pause_with_id.args.get("id").map(|s| s.as_str()),
Some("42")
);
// test play
let req_play = Request::play("/path/to/sound.mp3", true);
assert_eq!(req_play.name, "play");
assert_eq!(
req_play.args.get("file_path").map(|s| s.as_str()),
Some("/path/to/sound.mp3")
);
assert_eq!(
req_play.args.get("concurrent").map(|s| s.as_str()),
Some("true")
);
// test set_volume
let req_volume = Request::set_volume(0.8, Some(10));
assert_eq!(req_volume.name, "set_volume");
assert_eq!(
req_volume.args.get("volume").map(|s| s.as_str()),
Some("0.8")
);
assert_eq!(req_volume.args.get("id").map(|s| s.as_str()), Some("10"));
// test set_hotkey_action_and_key
let action = Request::ping();
let req_hotkey_action_and_key =
Request::set_hotkey_action_and_key("slot1", &action, "Ctrl+P");
assert_eq!(req_hotkey_action_and_key.name, "set_hotkey_action_and_key");
assert_eq!(
req_hotkey_action_and_key
.args
.get("slot")
.map(|s| s.as_str()),
Some("slot1")
);
assert_eq!(
req_hotkey_action_and_key
.args
.get("key_chord")
.map(|s| s.as_str()),
Some("Ctrl+P")
);
let action_json = serde_json::to_string(&action).unwrap();
assert_eq!(
req_hotkey_action_and_key
.args
.get("action")
.map(|s| s.as_str()),
Some(action_json.as_str())
);
}
}
+76
View File
@@ -199,3 +199,79 @@ pub async fn start_global_hotkey_listener() {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_modifier_state() {
let mut state = ModifierState::new();
assert!(!state.any_active());
// Press Ctrl
state.update(KeyCode::KEY_LEFTCTRL, true);
assert!(state.ctrl);
assert!(state.any_active());
// Release Ctrl
state.update(KeyCode::KEY_LEFTCTRL, false);
assert!(!state.ctrl);
assert!(!state.any_active());
// Press multiple modifiers
state.update(KeyCode::KEY_RIGHTALT, true);
state.update(KeyCode::KEY_LEFTSHIFT, true);
assert!(state.alt);
assert!(state.shift);
assert!(state.any_active());
// Update a non-modifier key
state.update(KeyCode::KEY_A, true);
// Modifier states should remain unchanged
assert!(state.alt);
assert!(state.shift);
}
#[test]
fn test_is_modifier() {
assert!(ModifierState::is_modifier(KeyCode::KEY_LEFTCTRL));
assert!(ModifierState::is_modifier(KeyCode::KEY_RIGHTCTRL));
assert!(ModifierState::is_modifier(KeyCode::KEY_LEFTALT));
assert!(ModifierState::is_modifier(KeyCode::KEY_RIGHTALT));
assert!(ModifierState::is_modifier(KeyCode::KEY_LEFTSHIFT));
assert!(ModifierState::is_modifier(KeyCode::KEY_RIGHTSHIFT));
assert!(ModifierState::is_modifier(KeyCode::KEY_LEFTMETA));
assert!(ModifierState::is_modifier(KeyCode::KEY_RIGHTMETA));
assert!(!ModifierState::is_modifier(KeyCode::KEY_A));
assert!(!ModifierState::is_modifier(KeyCode::KEY_1));
}
#[test]
fn test_evdev_key_name() {
assert_eq!(evdev_key_name(KeyCode::KEY_A), Some("A"));
assert_eq!(evdev_key_name(KeyCode::KEY_Z), Some("Z"));
assert_eq!(evdev_key_name(KeyCode::KEY_0), Some("0"));
assert_eq!(evdev_key_name(KeyCode::KEY_F1), Some("F1"));
assert_eq!(evdev_key_name(KeyCode::KEY_F12), Some("F12"));
assert_eq!(evdev_key_name(KeyCode::KEY_ENTER), None);
}
#[test]
fn test_build_chord() {
let mut modifiers = ModifierState::new();
assert_eq!(build_chord(&modifiers, "A"), "A");
modifiers.ctrl = true;
assert_eq!(build_chord(&modifiers, "A"), "Ctrl+A");
modifiers.shift = true;
assert_eq!(build_chord(&modifiers, "B"), "Ctrl+Shift+B");
modifiers.alt = true;
modifiers.meta = true;
assert_eq!(build_chord(&modifiers, "F5"), "Ctrl+Alt+Shift+Super+F5");
}
}
+13
View File
@@ -139,3 +139,16 @@ pub fn start_app_state_thread(audio_player_state_shared: Arc<Mutex<AudioPlayerSt
}
});
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_time_pair() {
assert_eq!(format_time_pair(0.0, 0.0), "00:00/00:00");
assert_eq!(format_time_pair(5.4, 10.0), "00:05/00:10");
assert_eq!(format_time_pair(59.9, 125.1), "01:00/02:05");
assert_eq!(format_time_pair(3600.0, 7205.0), "60:00/120:05");
}
}