merge: clear the clippy backlog and unify lock helpers (D1-warnings, D3)
51 clippy warnings -> 0, with 8 justified #[allow]s (IPC arity, specta wire types, and the 9 test-only await-holding-lock sites). 27 raw lock calls moved to the poison-tolerant helpers - all of them test code; production was already clean. Caught a non-neutral clippy --fix: removing the redundant 'use hostname;' in credentials.rs orphaned its #[cfg(target_os = "linux")] onto SERVICE_NAME, which would have cfg'd the constant out of every non-Linux build. Compiles clean on Linux, so only Windows/macOS CI would have caught it.
This commit is contained in:
@@ -6,6 +6,12 @@ use serde::{Deserialize, Serialize};
|
||||
/// Autoplay decision result - determines what happens after playback ends
|
||||
#[derive(specta::Type, Debug, Clone, Serialize)]
|
||||
#[serde(tag = "action", rename_all = "camelCase")]
|
||||
// `ShowNextEpisodePopup` carries two `MediaItem`s, so it dwarfs the unit
|
||||
// variants. Boxing them is not worth it here: this enum is constructed once per
|
||||
// end-of-item (never in a hot loop or a large collection), and it is an IPC type
|
||||
// — the indirection would have to stay invisible to serde/specta while every
|
||||
// match arm gained a deref, for no measurable gain.
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum AutoplayDecision {
|
||||
/// Stop playback (no next item or timer expired)
|
||||
Stop,
|
||||
|
||||
@@ -30,6 +30,12 @@ use super::{MediaSessionType, SleepTimerMode};
|
||||
// queue_changed never reach the frontend, so the mini player never appears).
|
||||
// Keep serde and specta agreeing: snake_case fields, snake_case variant tags.
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
// `ShowNextEpisodePopup` carries two `MediaItem`s, so it dwarfs the small
|
||||
// position/state variants. Boxing them is rejected deliberately: this is a
|
||||
// serde + specta wire type whose generated TypeScript must not shift, and the
|
||||
// events are emitted a few times a second at most — never bulk-allocated — so
|
||||
// the size difference costs nothing measurable.
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum PlayerStatusEvent {
|
||||
/// Playback position updated (emitted periodically during playback)
|
||||
PositionUpdate {
|
||||
|
||||
@@ -243,7 +243,7 @@ impl MpvBackend {
|
||||
});
|
||||
}
|
||||
}
|
||||
libmpv::events::Event::PropertyChange { name, .. } if name == "pause" => {
|
||||
libmpv::events::Event::PropertyChange { name: "pause", .. } => {
|
||||
// Handle pause state changes
|
||||
if let Ok(is_paused) = mpv.get_property::<bool>("pause") {
|
||||
let media_id = state
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
/// Tests for MpvBackend to prevent regressions
|
||||
///
|
||||
/// These tests are designed to catch common issues like:
|
||||
/// - Tokio runtime panics when spawning async tasks from std::thread
|
||||
/// - Position update thread failures
|
||||
/// - Event emission issues
|
||||
///
|
||||
/// TRACES: UR-003, UR-004 | IR-003 | IT-003, IT-004
|
||||
//! Tests for MpvBackend to prevent regressions
|
||||
//!
|
||||
//! These tests are designed to catch common issues like:
|
||||
//! - Tokio runtime panics when spawning async tasks from std::thread
|
||||
//! - Position update thread failures
|
||||
//! - Event emission issues
|
||||
//!
|
||||
//! TRACES: UR-003, UR-004 | IR-003 | IT-003, IT-004
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::utils::lock::MutexSafe;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tokio::sync::Mutex as TokioMutex;
|
||||
|
||||
@@ -67,14 +68,14 @@ mod tests {
|
||||
if let Ok(handle) = tokio::runtime::Handle::try_current() {
|
||||
// Has runtime (shouldn't happen in this test)
|
||||
handle.spawn(async move {
|
||||
*counter_clone.lock().unwrap() += 1;
|
||||
*counter_clone.lock_safe() += 1;
|
||||
});
|
||||
} else {
|
||||
// No runtime - use fallback (should happen in this test)
|
||||
std::thread::spawn(move || {
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
rt.block_on(async move {
|
||||
*counter_clone.lock().unwrap() += 1;
|
||||
*counter_clone.lock_safe() += 1;
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -85,7 +86,7 @@ mod tests {
|
||||
// Wait for async task to complete
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
|
||||
let count = *counter.lock().unwrap();
|
||||
let count = *counter.lock_safe();
|
||||
assert_eq!(
|
||||
count, 1,
|
||||
"Fallback pattern should execute async code successfully"
|
||||
@@ -109,13 +110,13 @@ mod tests {
|
||||
let position = i as f64 * 0.25;
|
||||
|
||||
// Store position (simulating event emission)
|
||||
positions_clone.lock().unwrap().push(position);
|
||||
positions_clone.lock_safe().push(position);
|
||||
}
|
||||
});
|
||||
|
||||
handle.join().unwrap();
|
||||
|
||||
let recorded_positions = positions.lock().unwrap();
|
||||
let recorded_positions = positions.lock_safe();
|
||||
assert_eq!(
|
||||
recorded_positions.len(),
|
||||
5,
|
||||
|
||||
@@ -806,11 +806,10 @@ mod tests {
|
||||
assert_eq!(queue.current_index(), Some(first_shuffled_index));
|
||||
|
||||
// Move through shuffle order
|
||||
for i in 1..shuffle_order.len() {
|
||||
for &expected_index in &shuffle_order[1..] {
|
||||
assert!(queue.has_next());
|
||||
let result = queue.next();
|
||||
assert!(result.is_some());
|
||||
let expected_index = shuffle_order[i];
|
||||
assert_eq!(queue.current_index(), Some(expected_index));
|
||||
}
|
||||
|
||||
|
||||
@@ -159,6 +159,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_end_reason_clone() {
|
||||
let reason = EndReason::Finished;
|
||||
// Deliberately exercising the derived `Clone` impl, not a plain copy:
|
||||
// `EndReason` is also `Copy`, so clippy flags the call as redundant.
|
||||
#[allow(clippy::clone_on_copy)]
|
||||
let cloned = reason.clone();
|
||||
assert_eq!(reason, cloned);
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ mod tests {
|
||||
|
||||
impl PlayerEventEmitter for RecordingEmitter {
|
||||
fn emit(&self, event: PlayerStatusEvent) {
|
||||
self.events.lock().unwrap().push(event);
|
||||
self.events.lock_safe().push(event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +244,7 @@ mod tests {
|
||||
let (mut b, events) = backend();
|
||||
b.load(&test_media()).unwrap();
|
||||
|
||||
let ev = events.lock().unwrap();
|
||||
let ev = events.lock_safe();
|
||||
let load = ev
|
||||
.iter()
|
||||
.find(|e| matches!(e, PlayerStatusEvent::WebviewAudioLoad { .. }))
|
||||
@@ -263,7 +263,7 @@ mod tests {
|
||||
b.pause().unwrap();
|
||||
b.seek(42.0).unwrap();
|
||||
|
||||
let ev = events.lock().unwrap();
|
||||
let ev = events.lock_safe();
|
||||
assert!(ev.iter().any(|e| matches!(
|
||||
e,
|
||||
PlayerStatusEvent::ControlCommand { action, .. } if action == "pause"
|
||||
|
||||
Reference in New Issue
Block a user