Skip to content

Commit 749a23d

Browse files
authored
expand test suite with new capability and playback tests
1 parent 5683d50 commit 749a23d

1 file changed

Lines changed: 110 additions & 20 deletions

File tree

tests/test_backend_trait.rs

Lines changed: 110 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ impl Backend for FakeBackend {
3434
Ok(RawDeviceInfo {
3535
name: "Fake Device".into(),
3636
capacity: 16,
37-
features: vec![u64::MAX],
37+
features: vec![u64::MAX, u64::MAX],
3838
})
3939
}
4040

@@ -76,6 +76,11 @@ impl Backend for FakeBackend {
7676
}
7777
}
7878

79+
fn is_playing(handle: &Self::Handle, id: i32) -> ShakeResult<bool> {
80+
let lock = handle.effects.lock().unwrap();
81+
Ok(lock.get(id as usize).and_then(|e| e.as_ref()).is_some())
82+
}
83+
7984
fn set_gain(_handle: &Self::Handle, _value: u16) -> ShakeResult<()> {
8085
Ok(())
8186
}
@@ -157,15 +162,15 @@ fn backend_capabilities_match_raw_features() {
157162
let handle = open_first().unwrap();
158163
let caps = FakeBackend::capabilities(&handle).unwrap();
159164

160-
// RawDeviceInfo.features = [u64::MAX], so all bits are set.
161165
assert!(caps.rumble);
162166
assert!(caps.periodic);
167+
assert!(caps.constant);
168+
assert!(caps.ramp);
163169
assert!(caps.spring);
164170
assert!(caps.friction);
165171
assert!(caps.damper);
166172
assert!(caps.inertia);
167173

168-
// max_effects must match RawDeviceInfo.capacity
169174
assert_eq!(caps.max_effects, 16);
170175
}
171176

@@ -176,9 +181,10 @@ fn backend_capabilities_are_stable() {
176181
let c1 = FakeBackend::capabilities(&handle).unwrap();
177182
let c2 = FakeBackend::capabilities(&handle).unwrap();
178183

179-
// Values must match exactly
180184
assert_eq!(c1.rumble, c2.rumble);
181185
assert_eq!(c1.periodic, c2.periodic);
186+
assert_eq!(c1.constant, c2.constant);
187+
assert_eq!(c1.ramp, c2.ramp);
182188
assert_eq!(c1.spring, c2.spring);
183189
assert_eq!(c1.friction, c2.friction);
184190
assert_eq!(c1.damper, c2.damper);
@@ -188,32 +194,26 @@ fn backend_capabilities_are_stable() {
188194

189195
#[test]
190196
fn backend_capabilities_handle_empty_feature_vector() {
191-
// Manually create a handle with no effects
192197
let handle = FakeHandle {
193198
effects: std::sync::Mutex::new(Vec::new()),
194199
};
195200

196-
// capabilities() must not panic even if the handle was not opened via scan/open
197201
let caps = FakeBackend::capabilities(&handle).unwrap();
198202

199-
// FakeBackend::query() always returns features = [u64::MAX]
200-
// so all capability bits must be true
201203
assert!(caps.rumble);
202204
assert!(caps.periodic);
203205
assert!(caps.spring);
204206
assert!(caps.friction);
205207
assert!(caps.damper);
206208
assert!(caps.inertia);
207209

208-
// max_effects must match RawDeviceInfo.capacity
209210
assert_eq!(caps.max_effects, 16);
210211
}
211212

212213
#[test]
213214
fn backend_capabilities_does_not_modify_effect_slots() {
214215
let handle = open_first().unwrap();
215216

216-
// Upload an effect
217217
let effect = Effect::Rumble(RumbleEffect {
218218
strong_magnitude: 1000,
219219
weak_magnitude: 500,
@@ -223,11 +223,7 @@ fn backend_capabilities_does_not_modify_effect_slots() {
223223
});
224224

225225
let id = FakeBackend::upload(&handle, &effect).unwrap();
226-
227-
// Calling capabilities() must not erase or modify effects
228226
let _ = FakeBackend::capabilities(&handle).unwrap();
229-
230-
// Effect must still be playable
231227
FakeBackend::play(&handle, id).unwrap();
232228
}
233229

@@ -297,8 +293,6 @@ fn backend_update_overwrites_effect() {
297293

298294
let id = FakeBackend::upload(&handle, &e1).unwrap();
299295
FakeBackend::update(&handle, id, &e2).unwrap();
300-
301-
// play() must succeed, meaning the slot still exists
302296
FakeBackend::play(&handle, id).unwrap();
303297
}
304298

@@ -318,20 +312,116 @@ fn backend_erase_does_not_shift_ids() {
318312
let id2 = FakeBackend::upload(&handle, &e).unwrap();
319313

320314
FakeBackend::erase(&handle, id1).unwrap();
321-
322-
// id2 must still be valid
323315
FakeBackend::play(&handle, id2).unwrap();
324316
}
325317

326318
#[test]
327319
fn backend_close_is_noop_and_safe() {
328320
let handle = open_first().unwrap();
329321

330-
// close must not panic
331322
FakeBackend::close(handle);
332323

333-
// open again must still work
334324
let handle2 = open_first().unwrap();
335325
let info = FakeBackend::query(&handle2).unwrap();
336326
assert_eq!(info.capacity, 16);
337327
}
328+
329+
#[test]
330+
fn backend_is_playing_reports_status_correctly() {
331+
let handle = open_first().unwrap();
332+
333+
let e = Effect::Rumble(RumbleEffect {
334+
strong_magnitude: 1000,
335+
weak_magnitude: 500,
336+
duration: 200,
337+
delay: 0,
338+
direction: 0,
339+
});
340+
341+
let id = FakeBackend::upload(&handle, &e).unwrap();
342+
assert!(FakeBackend::is_playing(&handle, id).unwrap());
343+
344+
FakeBackend::erase(&handle, id).unwrap();
345+
assert!(!FakeBackend::is_playing(&handle, id).unwrap());
346+
}
347+
348+
#[test]
349+
fn backend_capabilities_includes_constant_and_ramp() {
350+
let handle = open_first().unwrap();
351+
let caps = FakeBackend::capabilities(&handle).unwrap();
352+
353+
assert!(caps.constant);
354+
assert!(caps.ramp);
355+
}
356+
357+
#[test]
358+
fn backend_capabilities_parses_partial_feature_masks() {
359+
pub struct RestrictedBackend;
360+
361+
impl Backend for RestrictedBackend {
362+
type Handle = FakeHandle;
363+
364+
fn scan() -> ShakeResult<Vec<PathBuf>> { Ok(vec![PathBuf::from("/dev/fake_restricted")]) }
365+
fn open(_path: &Path) -> ShakeResult<Self::Handle> { Ok(FakeHandle { effects: std::sync::Mutex::new(Vec::new()) }) }
366+
fn close(_handle: Self::Handle) {}
367+
368+
fn query(_handle: &Self::Handle) -> ShakeResult<RawDeviceInfo> {
369+
let mut features = vec![0u64, 0u64];
370+
features[1] |= 1 << (80 % 64); // FF_RUMBLE
371+
372+
Ok(RawDeviceInfo {
373+
name: "Restricted Device".into(),
374+
capacity: 4,
375+
features,
376+
})
377+
}
378+
379+
fn upload(_: &Self::Handle, _: &Effect) -> ShakeResult<i32> { Ok(0) }
380+
fn update(_: &Self::Handle, _: i32, _: &Effect) -> ShakeResult<()> { Ok(()) }
381+
fn play(_: &Self::Handle, _: i32) -> ShakeResult<()> { Ok(()) }
382+
fn stop(_: &Self::Handle, _: i32) -> ShakeResult<()> { Ok(()) }
383+
fn erase(_: &Self::Handle, _: i32) -> ShakeResult<()> { Ok(()) }
384+
fn is_playing(_: &Self::Handle, _: i32) -> ShakeResult<bool> { Ok(false) }
385+
fn set_gain(_: &Self::Handle, _: u16) -> ShakeResult<()> { Ok(()) }
386+
fn set_autocenter(_: &Self::Handle, _: u16) -> ShakeResult<()> { Ok(()) }
387+
}
388+
389+
let handle = RestrictedBackend::open(Path::new("/dev/fake_restricted")).unwrap();
390+
let caps = RestrictedBackend::capabilities(&handle).unwrap();
391+
392+
assert!(caps.rumble);
393+
assert!(!caps.periodic);
394+
assert!(!caps.constant);
395+
assert!(!caps.spring);
396+
}
397+
398+
#[test]
399+
fn backend_capabilities_handles_empty_or_short_feature_vectors() {
400+
struct EmptyBackend;
401+
impl Backend for EmptyBackend {
402+
type Handle = FakeHandle;
403+
fn scan() -> ShakeResult<Vec<PathBuf>> { Ok(vec![PathBuf::from("/dev/empty")]) }
404+
fn open(_: &Path) -> ShakeResult<Self::Handle> { Ok(FakeHandle { effects: std::sync::Mutex::new(Vec::new()) }) }
405+
fn close(_: Self::Handle) {}
406+
fn query(_: &Self::Handle) -> ShakeResult<RawDeviceInfo> {
407+
Ok(RawDeviceInfo { name: "Empty".into(), capacity: 0, features: vec![] })
408+
}
409+
fn upload(_: &Self::Handle, _: &Effect) -> ShakeResult<i32> { Ok(0) }
410+
fn update(_: &Self::Handle, _: i32, _: &Effect) -> ShakeResult<()> { Ok(()) }
411+
fn play(_: &Self::Handle, _: i32) -> ShakeResult<()> { Ok(()) }
412+
fn stop(_: &Self::Handle, _: i32) -> ShakeResult<()> { Ok(()) }
413+
fn erase(_: &Self::Handle, _: i32) -> ShakeResult<()> { Ok(()) }
414+
fn is_playing(_: &Self::Handle, _: i32) -> ShakeResult<bool> { Ok(false) }
415+
fn set_gain(_: &Self::Handle, _: u16) -> ShakeResult<()> { Ok(()) }
416+
fn set_autocenter(_: &Self::Handle, _: u16) -> ShakeResult<()> { Ok(()) }
417+
}
418+
419+
let handle = EmptyBackend::open(Path::new("/dev/empty")).unwrap();
420+
let caps = EmptyBackend::capabilities(&handle).unwrap();
421+
422+
assert!(!caps.rumble);
423+
assert!(!caps.periodic);
424+
assert!(!caps.constant);
425+
assert!(!caps.ramp);
426+
assert_eq!(caps.max_effects, 0);
427+
}

0 commit comments

Comments
 (0)