1extern crate alloc;
36
37use alloc::string::String;
38use alloc::vec;
39
40use crate::driver::DriverInfo;
41use crate::error::OsStatus;
42use crate::object::{AudioObjectId, ObjectKind};
43use crate::objects::{Object, ObjectMap};
44use crate::property::{PropertyAddress, PropertySelector, PropertyValue, ValueRange};
45use crate::stream::StreamDirection;
46
47pub const TRANSPORT_TYPE_VIRTUAL: u32 = u32::from_be_bytes(*b"virt");
52
53pub const TERMINAL_TYPE_UNKNOWN: u32 = 0;
58
59#[derive(Copy, Clone, PartialEq, Debug)]
70pub struct DeviceState {
71 pub sample_rate: f64,
74 pub running: bool,
77}
78
79impl DeviceState {
80 #[inline]
83 #[must_use]
84 pub fn from_spec(spec: &crate::device::DeviceSpec) -> Self {
85 Self {
86 sample_rate: spec.sample_rate(),
87 running: false,
88 }
89 }
90}
91
92pub fn get_property_data(
107 map: &ObjectMap,
108 info: &DriverInfo,
109 state: &DeviceState,
110 object_id: AudioObjectId,
111 address: &PropertyAddress,
112) -> Result<PropertyValue, OsStatus> {
113 match map.resolve(object_id).ok_or(OsStatus::BAD_OBJECT)? {
114 Object::PlugIn => plugin_property(map, info, address),
115 Object::Device => device_property(map, state, address),
116 Object::Stream(direction) => stream_property(map, direction, address),
117 }
118}
119
120pub fn property_data_size(
127 map: &ObjectMap,
128 info: &DriverInfo,
129 state: &DeviceState,
130 object_id: AudioObjectId,
131 address: &PropertyAddress,
132) -> Result<usize, OsStatus> {
133 get_property_data(map, info, state, object_id, address).map(|value| value.byte_size())
134}
135
136pub fn is_property_settable(
145 map: &ObjectMap,
146 info: &DriverInfo,
147 state: &DeviceState,
148 object_id: AudioObjectId,
149 address: &PropertyAddress,
150) -> Result<bool, OsStatus> {
151 get_property_data(map, info, state, object_id, address)?;
155 let object = map.resolve(object_id).ok_or(OsStatus::BAD_OBJECT)?;
156 Ok(matches!(
157 (object, address.selector),
158 (Object::Device, PropertySelector::DEVICE_NOMINAL_SAMPLE_RATE)
159 ))
160}
161
162pub fn set_property_data(
180 map: &ObjectMap,
181 info: &DriverInfo,
182 state: &mut DeviceState,
183 object_id: AudioObjectId,
184 address: &PropertyAddress,
185 data: &[u8],
186) -> Result<(), OsStatus> {
187 if !is_property_settable(map, info, state, object_id, address)? {
191 return Err(OsStatus::ILLEGAL_OPERATION);
192 }
193 debug_assert_eq!(
198 (
199 map.resolve(object_id),
200 address.selector == PropertySelector::DEVICE_NOMINAL_SAMPLE_RATE
201 ),
202 (Some(Object::Device), true),
203 );
204
205 let rate = read_f64(data)?;
206 if rate != map.spec().sample_rate() {
209 return Err(OsStatus::UNSUPPORTED_FORMAT);
210 }
211 state.sample_rate = rate;
212 Ok(())
213}
214
215fn read_f64(data: &[u8]) -> Result<f64, OsStatus> {
219 let bytes: [u8; 8] = data
220 .get(..8)
221 .ok_or(OsStatus::BAD_PROPERTY_SIZE)?
222 .try_into()
223 .expect("slice of length 8 converts to [u8; 8]");
224 Ok(f64::from_ne_bytes(bytes))
225}
226
227fn zero_timestamp_period(spec: &crate::device::DeviceSpec) -> u32 {
234 spec.sample_rate() as u32
235}
236
237fn plugin_property(
238 map: &ObjectMap,
239 info: &DriverInfo,
240 address: &PropertyAddress,
241) -> Result<PropertyValue, OsStatus> {
242 use PropertySelector as S;
243 Ok(match address.selector {
244 S::BASE_CLASS => PropertyValue::U32(ObjectKind::PlugIn.base_class_id().as_u32()),
245 S::CLASS => PropertyValue::U32(ObjectKind::PlugIn.class_id().as_u32()),
246 S::OWNER => PropertyValue::ObjectId(AudioObjectId::UNKNOWN),
249 S::NAME => PropertyValue::Text(String::from(info.name)),
250 S::MANUFACTURER => PropertyValue::Text(String::from(info.manufacturer)),
251 S::OWNED_OBJECTS => {
252 PropertyValue::ObjectList(map.owned_objects(map.plugin_id(), address.scope))
253 }
254 S::PLUGIN_DEVICE_LIST => PropertyValue::ObjectList(vec![map.device_id()]),
255 S::PLUGIN_BOX_LIST => PropertyValue::ObjectList(vec![]),
257 _ => return Err(OsStatus::UNKNOWN_PROPERTY),
258 })
259}
260
261fn device_property(
262 map: &ObjectMap,
263 state: &DeviceState,
264 address: &PropertyAddress,
265) -> Result<PropertyValue, OsStatus> {
266 use PropertySelector as S;
267 let spec = map.spec();
268 Ok(match address.selector {
269 S::BASE_CLASS => PropertyValue::U32(ObjectKind::Device.base_class_id().as_u32()),
270 S::CLASS => PropertyValue::U32(ObjectKind::Device.class_id().as_u32()),
271 S::OWNER => PropertyValue::ObjectId(map.plugin_id()),
272 S::NAME => PropertyValue::Text(String::from(spec.name())),
273 S::MANUFACTURER => PropertyValue::Text(String::from(spec.manufacturer())),
274 S::OWNED_OBJECTS => {
275 PropertyValue::ObjectList(map.owned_objects(map.device_id(), address.scope))
276 }
277 S::DEVICE_UID => PropertyValue::Text(String::from(spec.uid())),
278 S::DEVICE_MODEL_UID => PropertyValue::Text(String::from(spec.uid())),
281 S::DEVICE_TRANSPORT_TYPE => PropertyValue::U32(TRANSPORT_TYPE_VIRTUAL),
282 S::DEVICE_IS_ALIVE => PropertyValue::U32(1),
284 S::DEVICE_IS_RUNNING => PropertyValue::U32(u32::from(state.running)),
285 S::DEVICE_STREAMS => PropertyValue::ObjectList(map.device_streams(address.scope)),
286 S::DEVICE_NOMINAL_SAMPLE_RATE => PropertyValue::F64(state.sample_rate),
287 S::DEVICE_AVAILABLE_SAMPLE_RATES => {
290 PropertyValue::RangeList(vec![ValueRange::point(spec.sample_rate())])
291 }
292 S::DEVICE_LATENCY | S::DEVICE_SAFETY_OFFSET => PropertyValue::U32(0),
295 S::DEVICE_ZERO_TIMESTAMP_PERIOD => PropertyValue::U32(zero_timestamp_period(spec)),
296 _ => return Err(OsStatus::UNKNOWN_PROPERTY),
297 })
298}
299
300fn stream_property(
301 map: &ObjectMap,
302 direction: StreamDirection,
303 address: &PropertyAddress,
304) -> Result<PropertyValue, OsStatus> {
305 use PropertySelector as S;
306 let stream = map.stream_spec(direction).ok_or(OsStatus::BAD_STREAM)?;
307 Ok(match address.selector {
308 S::BASE_CLASS => PropertyValue::U32(ObjectKind::Stream.base_class_id().as_u32()),
309 S::CLASS => PropertyValue::U32(ObjectKind::Stream.class_id().as_u32()),
310 S::OWNER => PropertyValue::ObjectId(map.device_id()),
311 S::OWNED_OBJECTS => PropertyValue::ObjectList(vec![]),
313 S::STREAM_DIRECTION => PropertyValue::U32(direction.as_property_value()),
314 S::STREAM_TERMINAL_TYPE => PropertyValue::U32(TERMINAL_TYPE_UNKNOWN),
315 S::STREAM_STARTING_CHANNEL => PropertyValue::U32(stream.starting_channel()),
316 S::STREAM_VIRTUAL_FORMAT | S::STREAM_PHYSICAL_FORMAT => {
319 PropertyValue::Format(stream.format())
320 }
321 _ => return Err(OsStatus::UNKNOWN_PROPERTY),
322 })
323}
324
325#[cfg(test)]
326mod tests {
327 use super::*;
328 use crate::device::DeviceSpec;
329 use crate::format::StreamFormat;
330 use crate::object::ObjectKind;
331 use crate::property::{PropertyScope, PropertyValue};
332 use crate::stream::StreamSpec;
333
334 fn loopback_spec() -> DeviceSpec {
335 let format = StreamFormat::float32(48_000.0, 2);
336 DeviceSpec::new("com.example.loopback", "Example Loopback", "Acme Audio")
337 .with_sample_rate(48_000.0)
338 .with_input(StreamSpec::input(format))
339 .with_output(StreamSpec::output(format))
340 }
341
342 fn info() -> DriverInfo {
343 DriverInfo {
344 name: "Example Driver",
345 manufacturer: "Acme Audio",
346 version: "1.0.0",
347 }
348 }
349
350 fn fixture() -> (ObjectMap, DriverInfo, DeviceState) {
351 let map = ObjectMap::new(loopback_spec());
352 let state = DeviceState::from_spec(map.spec());
353 (map, info(), state)
354 }
355
356 fn get(
357 map: &ObjectMap,
358 info: &DriverInfo,
359 state: &DeviceState,
360 id: AudioObjectId,
361 selector: PropertySelector,
362 ) -> Result<PropertyValue, OsStatus> {
363 get_property_data(map, info, state, id, &PropertyAddress::global(selector))
364 }
365
366 #[test]
367 fn unknown_object_is_bad_object() {
368 let (map, info, state) = fixture();
369 assert_eq!(
370 get(
371 &map,
372 &info,
373 &state,
374 AudioObjectId::from_u32(999),
375 PropertySelector::CLASS
376 ),
377 Err(OsStatus::BAD_OBJECT)
378 );
379 }
380
381 #[test]
382 fn unknown_selector_is_unknown_property() {
383 let (map, info, state) = fixture();
384 let bogus = PropertySelector::new(*b"zzzz");
385 assert_eq!(
386 get(&map, &info, &state, map.plugin_id(), bogus),
387 Err(OsStatus::UNKNOWN_PROPERTY)
388 );
389 assert_eq!(
390 get(&map, &info, &state, map.device_id(), bogus),
391 Err(OsStatus::UNKNOWN_PROPERTY)
392 );
393 }
394
395 #[test]
396 fn plugin_class_and_base_class() {
397 let (map, info, state) = fixture();
398 assert_eq!(
399 get(
400 &map,
401 &info,
402 &state,
403 map.plugin_id(),
404 PropertySelector::CLASS
405 ),
406 Ok(PropertyValue::U32(ObjectKind::PlugIn.class_id().as_u32()))
407 );
408 assert_eq!(
409 get(
410 &map,
411 &info,
412 &state,
413 map.plugin_id(),
414 PropertySelector::BASE_CLASS
415 ),
416 Ok(PropertyValue::U32(
417 ObjectKind::PlugIn.base_class_id().as_u32()
418 ))
419 );
420 }
421
422 #[test]
423 fn plugin_has_no_owner() {
424 let (map, info, state) = fixture();
425 assert_eq!(
426 get(
427 &map,
428 &info,
429 &state,
430 map.plugin_id(),
431 PropertySelector::OWNER
432 ),
433 Ok(PropertyValue::ObjectId(AudioObjectId::UNKNOWN))
434 );
435 }
436
437 #[test]
438 fn plugin_identity_comes_from_driver_info() {
439 let (map, info, state) = fixture();
440 assert_eq!(
441 get(&map, &info, &state, map.plugin_id(), PropertySelector::NAME),
442 Ok(PropertyValue::Text("Example Driver".into()))
443 );
444 assert_eq!(
445 get(
446 &map,
447 &info,
448 &state,
449 map.plugin_id(),
450 PropertySelector::MANUFACTURER
451 ),
452 Ok(PropertyValue::Text("Acme Audio".into()))
453 );
454 }
455
456 #[test]
457 fn plugin_device_list_and_owned_objects_point_at_the_device() {
458 let (map, info, state) = fixture();
459 let expected = PropertyValue::ObjectList(vec![map.device_id()]);
460 assert_eq!(
461 get(
462 &map,
463 &info,
464 &state,
465 map.plugin_id(),
466 PropertySelector::PLUGIN_DEVICE_LIST
467 ),
468 Ok(expected.clone())
469 );
470 assert_eq!(
471 get(
472 &map,
473 &info,
474 &state,
475 map.plugin_id(),
476 PropertySelector::OWNED_OBJECTS
477 ),
478 Ok(expected)
479 );
480 }
481
482 #[test]
483 fn plugin_box_list_is_empty() {
484 let (map, info, state) = fixture();
485 assert_eq!(
486 get(
487 &map,
488 &info,
489 &state,
490 map.plugin_id(),
491 PropertySelector::PLUGIN_BOX_LIST
492 ),
493 Ok(PropertyValue::ObjectList(vec![]))
494 );
495 }
496
497 #[test]
498 fn device_identity_comes_from_the_spec() {
499 let (map, info, state) = fixture();
500 let dev = map.device_id();
501 assert_eq!(
502 get(&map, &info, &state, dev, PropertySelector::NAME),
503 Ok(PropertyValue::Text("Example Loopback".into()))
504 );
505 assert_eq!(
506 get(&map, &info, &state, dev, PropertySelector::DEVICE_UID),
507 Ok(PropertyValue::Text("com.example.loopback".into()))
508 );
509 assert_eq!(
510 get(&map, &info, &state, dev, PropertySelector::DEVICE_MODEL_UID),
511 Ok(PropertyValue::Text("com.example.loopback".into()))
512 );
513 assert_eq!(
514 get(&map, &info, &state, dev, PropertySelector::MANUFACTURER),
515 Ok(PropertyValue::Text("Acme Audio".into()))
516 );
517 }
518
519 #[test]
520 fn device_owner_is_the_plugin() {
521 let (map, info, state) = fixture();
522 assert_eq!(
523 get(
524 &map,
525 &info,
526 &state,
527 map.device_id(),
528 PropertySelector::OWNER
529 ),
530 Ok(PropertyValue::ObjectId(map.plugin_id()))
531 );
532 }
533
534 #[test]
535 fn device_transport_type_is_virtual() {
536 let (map, info, state) = fixture();
537 assert_eq!(
538 get(
539 &map,
540 &info,
541 &state,
542 map.device_id(),
543 PropertySelector::DEVICE_TRANSPORT_TYPE
544 ),
545 Ok(PropertyValue::U32(u32::from_be_bytes(*b"virt")))
546 );
547 }
548
549 #[test]
550 fn device_is_alive_and_reflects_running_state() {
551 let (map, info, mut state) = fixture();
552 let dev = map.device_id();
553 assert_eq!(
554 get(&map, &info, &state, dev, PropertySelector::DEVICE_IS_ALIVE),
555 Ok(PropertyValue::U32(1))
556 );
557 assert_eq!(
558 get(
559 &map,
560 &info,
561 &state,
562 dev,
563 PropertySelector::DEVICE_IS_RUNNING
564 ),
565 Ok(PropertyValue::U32(0))
566 );
567 state.running = true;
568 assert_eq!(
569 get(
570 &map,
571 &info,
572 &state,
573 dev,
574 PropertySelector::DEVICE_IS_RUNNING
575 ),
576 Ok(PropertyValue::U32(1))
577 );
578 }
579
580 #[test]
581 fn device_sample_rate_tracks_runtime_state() {
582 let (map, info, mut state) = fixture();
583 let dev = map.device_id();
584 assert_eq!(
585 get(
586 &map,
587 &info,
588 &state,
589 dev,
590 PropertySelector::DEVICE_NOMINAL_SAMPLE_RATE
591 ),
592 Ok(PropertyValue::F64(48_000.0))
593 );
594 state.sample_rate = 96_000.0;
596 assert_eq!(
597 get(
598 &map,
599 &info,
600 &state,
601 dev,
602 PropertySelector::DEVICE_NOMINAL_SAMPLE_RATE
603 ),
604 Ok(PropertyValue::F64(96_000.0))
605 );
606 assert_eq!(
608 get(
609 &map,
610 &info,
611 &state,
612 dev,
613 PropertySelector::DEVICE_AVAILABLE_SAMPLE_RATES
614 ),
615 Ok(PropertyValue::RangeList(vec![ValueRange::point(48_000.0)]))
616 );
617 }
618
619 #[test]
620 fn device_streams_filter_by_scope() {
621 let (map, info, state) = fixture();
622 let dev = map.device_id();
623 let global = PropertyAddress::new(
624 PropertySelector::DEVICE_STREAMS,
625 PropertyScope::GLOBAL,
626 crate::property::PropertyElement::MAIN,
627 );
628 let input = PropertyAddress::new(
629 PropertySelector::DEVICE_STREAMS,
630 PropertyScope::INPUT,
631 crate::property::PropertyElement::MAIN,
632 );
633 assert_eq!(
634 get_property_data(&map, &info, &state, dev, &global),
635 Ok(PropertyValue::ObjectList(vec![
636 map.stream_id(StreamDirection::Input).unwrap(),
637 map.stream_id(StreamDirection::Output).unwrap(),
638 ]))
639 );
640 assert_eq!(
641 get_property_data(&map, &info, &state, dev, &input),
642 Ok(PropertyValue::ObjectList(vec![map
643 .stream_id(StreamDirection::Input)
644 .unwrap()]))
645 );
646 }
647
648 #[test]
649 fn device_latency_and_safety_offset_are_zero() {
650 let (map, info, state) = fixture();
651 let dev = map.device_id();
652 assert_eq!(
653 get(&map, &info, &state, dev, PropertySelector::DEVICE_LATENCY),
654 Ok(PropertyValue::U32(0))
655 );
656 assert_eq!(
657 get(
658 &map,
659 &info,
660 &state,
661 dev,
662 PropertySelector::DEVICE_SAFETY_OFFSET
663 ),
664 Ok(PropertyValue::U32(0))
665 );
666 }
667
668 #[test]
669 fn device_zero_timestamp_period_is_one_second_of_frames() {
670 let (map, info, state) = fixture();
671 assert_eq!(
672 get(
673 &map,
674 &info,
675 &state,
676 map.device_id(),
677 PropertySelector::DEVICE_ZERO_TIMESTAMP_PERIOD
678 ),
679 Ok(PropertyValue::U32(48_000))
680 );
681 }
682
683 #[test]
684 fn stream_class_owner_and_direction() {
685 let (map, info, state) = fixture();
686 let input = map.stream_id(StreamDirection::Input).unwrap();
687 let output = map.stream_id(StreamDirection::Output).unwrap();
688 assert_eq!(
689 get(&map, &info, &state, input, PropertySelector::CLASS),
690 Ok(PropertyValue::U32(ObjectKind::Stream.class_id().as_u32()))
691 );
692 assert_eq!(
693 get(&map, &info, &state, input, PropertySelector::OWNER),
694 Ok(PropertyValue::ObjectId(map.device_id()))
695 );
696 assert_eq!(
697 get(
698 &map,
699 &info,
700 &state,
701 input,
702 PropertySelector::STREAM_DIRECTION
703 ),
704 Ok(PropertyValue::U32(1))
705 );
706 assert_eq!(
707 get(
708 &map,
709 &info,
710 &state,
711 output,
712 PropertySelector::STREAM_DIRECTION
713 ),
714 Ok(PropertyValue::U32(0))
715 );
716 }
717
718 #[test]
719 fn stream_formats_come_from_the_stream_spec() {
720 let (map, info, state) = fixture();
721 let input = map.stream_id(StreamDirection::Input).unwrap();
722 let expected = PropertyValue::Format(StreamFormat::float32(48_000.0, 2));
723 assert_eq!(
724 get(
725 &map,
726 &info,
727 &state,
728 input,
729 PropertySelector::STREAM_VIRTUAL_FORMAT
730 ),
731 Ok(expected.clone())
732 );
733 assert_eq!(
734 get(
735 &map,
736 &info,
737 &state,
738 input,
739 PropertySelector::STREAM_PHYSICAL_FORMAT
740 ),
741 Ok(expected)
742 );
743 }
744
745 #[test]
746 fn stream_starting_channel_comes_from_the_spec() {
747 let format = StreamFormat::float32(48_000.0, 2);
748 let spec = DeviceSpec::new("uid", "name", "maker")
749 .with_output(StreamSpec::output(format).with_starting_channel(5));
750 let map = ObjectMap::new(spec);
751 let state = DeviceState::from_spec(map.spec());
752 let output = map.stream_id(StreamDirection::Output).unwrap();
753 assert_eq!(
754 get(
755 &map,
756 &info(),
757 &state,
758 output,
759 PropertySelector::STREAM_STARTING_CHANNEL
760 ),
761 Ok(PropertyValue::U32(5))
762 );
763 }
764
765 #[test]
766 fn property_data_size_matches_the_value_size() {
767 let (map, info, state) = fixture();
768 let dev = map.device_id();
769 assert_eq!(
771 property_data_size(
772 &map,
773 &info,
774 &state,
775 dev,
776 &PropertyAddress::global(PropertySelector::DEVICE_NOMINAL_SAMPLE_RATE)
777 ),
778 Ok(8)
779 );
780 assert_eq!(
782 property_data_size(
783 &map,
784 &info,
785 &state,
786 dev,
787 &PropertyAddress::global(PropertySelector::DEVICE_STREAMS)
788 ),
789 Ok(8)
790 );
791 let input = map.stream_id(StreamDirection::Input).unwrap();
793 assert_eq!(
794 property_data_size(
795 &map,
796 &info,
797 &state,
798 input,
799 &PropertyAddress::global(PropertySelector::STREAM_VIRTUAL_FORMAT)
800 ),
801 Ok(40)
802 );
803 }
804
805 #[test]
806 fn property_data_size_propagates_errors() {
807 let (map, info, state) = fixture();
808 assert_eq!(
809 property_data_size(
810 &map,
811 &info,
812 &state,
813 AudioObjectId::from_u32(999),
814 &PropertyAddress::global(PropertySelector::CLASS)
815 ),
816 Err(OsStatus::BAD_OBJECT)
817 );
818 }
819
820 #[test]
821 fn only_the_sample_rate_is_settable() {
822 let (map, info, state) = fixture();
823 let dev = map.device_id();
824 assert_eq!(
825 is_property_settable(
826 &map,
827 &info,
828 &state,
829 dev,
830 &PropertyAddress::global(PropertySelector::DEVICE_NOMINAL_SAMPLE_RATE)
831 ),
832 Ok(true)
833 );
834 for selector in [
836 PropertySelector::DEVICE_UID,
837 PropertySelector::NAME,
838 PropertySelector::DEVICE_STREAMS,
839 PropertySelector::CLASS,
840 ] {
841 assert_eq!(
842 is_property_settable(&map, &info, &state, dev, &PropertyAddress::global(selector)),
843 Ok(false),
844 "{selector:?} should be read-only"
845 );
846 }
847 }
848
849 #[test]
850 fn is_property_settable_rejects_unknown_object_and_property() {
851 let (map, info, state) = fixture();
852 assert_eq!(
853 is_property_settable(
854 &map,
855 &info,
856 &state,
857 AudioObjectId::from_u32(999),
858 &PropertyAddress::global(PropertySelector::CLASS)
859 ),
860 Err(OsStatus::BAD_OBJECT)
861 );
862 assert_eq!(
863 is_property_settable(
864 &map,
865 &info,
866 &state,
867 map.device_id(),
868 &PropertyAddress::global(PropertySelector::new(*b"zzzz"))
869 ),
870 Err(OsStatus::UNKNOWN_PROPERTY)
871 );
872 }
873
874 #[test]
875 fn device_state_from_spec_is_idle_at_nominal_rate() {
876 let spec = loopback_spec();
877 let state = DeviceState::from_spec(&spec);
878 assert_eq!(state.sample_rate, 48_000.0);
879 assert!(!state.running);
880 }
881
882 #[test]
883 fn set_sample_rate_applies_a_supported_rate() {
884 let (map, info, mut state) = fixture();
885 assert_eq!(
887 set_property_data(
888 &map,
889 &info,
890 &mut state,
891 map.device_id(),
892 &PropertyAddress::global(PropertySelector::DEVICE_NOMINAL_SAMPLE_RATE),
893 &48_000.0_f64.to_ne_bytes(),
894 ),
895 Ok(())
896 );
897 assert_eq!(state.sample_rate, 48_000.0);
898 }
899
900 #[test]
901 fn set_sample_rate_rejects_an_unsupported_rate() {
902 let (map, info, mut state) = fixture();
903 assert_eq!(
904 set_property_data(
905 &map,
906 &info,
907 &mut state,
908 map.device_id(),
909 &PropertyAddress::global(PropertySelector::DEVICE_NOMINAL_SAMPLE_RATE),
910 &96_000.0_f64.to_ne_bytes(),
911 ),
912 Err(OsStatus::UNSUPPORTED_FORMAT)
913 );
914 assert_eq!(state.sample_rate, 48_000.0);
916 }
917
918 #[test]
919 fn set_sample_rate_rejects_an_undersized_buffer() {
920 let (map, info, mut state) = fixture();
921 assert_eq!(
922 set_property_data(
923 &map,
924 &info,
925 &mut state,
926 map.device_id(),
927 &PropertyAddress::global(PropertySelector::DEVICE_NOMINAL_SAMPLE_RATE),
928 &[0u8; 4],
929 ),
930 Err(OsStatus::BAD_PROPERTY_SIZE)
931 );
932 }
933
934 #[test]
935 fn set_read_only_property_is_illegal() {
936 let (map, info, mut state) = fixture();
937 assert_eq!(
939 set_property_data(
940 &map,
941 &info,
942 &mut state,
943 map.device_id(),
944 &PropertyAddress::global(PropertySelector::DEVICE_UID),
945 b"whatever",
946 ),
947 Err(OsStatus::ILLEGAL_OPERATION)
948 );
949 }
950
951 #[test]
952 fn set_rejects_unknown_object_and_property() {
953 let (map, info, mut state) = fixture();
954 assert_eq!(
955 set_property_data(
956 &map,
957 &info,
958 &mut state,
959 AudioObjectId::from_u32(999),
960 &PropertyAddress::global(PropertySelector::DEVICE_NOMINAL_SAMPLE_RATE),
961 &48_000.0_f64.to_ne_bytes(),
962 ),
963 Err(OsStatus::BAD_OBJECT)
964 );
965 assert_eq!(
966 set_property_data(
967 &map,
968 &info,
969 &mut state,
970 map.device_id(),
971 &PropertyAddress::global(PropertySelector::new(*b"zzzz")),
972 &48_000.0_f64.to_ne_bytes(),
973 ),
974 Err(OsStatus::UNKNOWN_PROPERTY)
975 );
976 }
977}