Skip to main content

linux_cec/
message.rs

1/*
2 * Copyright © 2024 Valve Software
3 * SPDX-License-Identifier: LGPL-2.1-or-later
4 */
5
6//! Message encoding, decoding and handling
7
8#![allow(clippy::len_without_is_empty)]
9
10#[cfg(test)]
11use linux_cec_macros::message_test;
12use linux_cec_macros::{MessageEnum, Operand};
13use num_enum::{IntoPrimitive, TryFromPrimitive};
14#[cfg(test)]
15use std::str::FromStr;
16
17use crate::operand::OperandEncodable;
18use crate::{cdc, constants, operand, AddressingType, PhysicalAddress, Result};
19#[cfg(test)]
20use crate::{Error, Range};
21
22pub use crate::cdc::{Message as CdcMessage, Opcode as CdcOpcode};
23
24#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, MessageEnum)]
25#[repr(u8)]
26#[non_exhaustive]
27pub enum Message {
28    #[addressing = "broadcast"]
29    ActiveSource {
30        address: PhysicalAddress,
31    } = constants::CEC_MSG_ACTIVE_SOURCE,
32    ImageViewOn = constants::CEC_MSG_IMAGE_VIEW_ON,
33    TextViewOn = constants::CEC_MSG_TEXT_VIEW_ON,
34    InactiveSource {
35        address: PhysicalAddress,
36    } = constants::CEC_MSG_INACTIVE_SOURCE,
37    #[addressing = "broadcast"]
38    RequestActiveSource = constants::CEC_MSG_REQUEST_ACTIVE_SOURCE,
39    #[addressing = "broadcast"]
40    RoutingChange {
41        original_address: PhysicalAddress,
42        new_address: PhysicalAddress,
43    } = constants::CEC_MSG_ROUTING_CHANGE,
44    #[addressing = "broadcast"]
45    RoutingInformation {
46        address: PhysicalAddress,
47    } = constants::CEC_MSG_ROUTING_INFORMATION,
48    #[addressing = "broadcast"]
49    SetStreamPath {
50        address: PhysicalAddress,
51    } = constants::CEC_MSG_SET_STREAM_PATH,
52    #[addressing = "either"]
53    Standby = constants::CEC_MSG_STANDBY,
54    RecordOff = constants::CEC_MSG_RECORD_OFF,
55    RecordOn {
56        source: operand::RecordSource,
57    } = constants::CEC_MSG_RECORD_ON,
58    RecordStatus {
59        status: operand::RecordStatusInfo,
60    } = constants::CEC_MSG_RECORD_STATUS,
61    RecordTvScreen = constants::CEC_MSG_RECORD_TV_SCREEN,
62    ClearAnalogueTimer {
63        day_of_month: operand::DayOfMonth,
64        month_of_year: operand::MonthOfYear,
65        start_time: operand::Time,
66        duration: operand::Duration,
67        recording_sequence: operand::RecordingSequence,
68        service_id: operand::AnalogueServiceId,
69    } = constants::CEC_MSG_CLEAR_ANALOGUE_TIMER,
70    ClearDigitalTimer {
71        day_of_month: operand::DayOfMonth,
72        month_of_year: operand::MonthOfYear,
73        start_time: operand::Time,
74        duration: operand::Duration,
75        recording_sequence: operand::RecordingSequence,
76        service_id: operand::DigitalServiceId,
77    } = constants::CEC_MSG_CLEAR_DIGITAL_TIMER,
78    ClearExtTimer {
79        day_of_month: operand::DayOfMonth,
80        month_of_year: operand::MonthOfYear,
81        start_time: operand::Time,
82        duration: operand::Duration,
83        recording_sequence: operand::RecordingSequence,
84        external_source: operand::ExternalSource,
85    } = constants::CEC_MSG_CLEAR_EXT_TIMER,
86    SetAnalogueTimer {
87        day_of_month: operand::DayOfMonth,
88        month_of_year: operand::MonthOfYear,
89        start_time: operand::Time,
90        duration: operand::Duration,
91        recording_sequence: operand::RecordingSequence,
92        service_id: operand::AnalogueServiceId,
93    } = constants::CEC_MSG_SET_ANALOGUE_TIMER,
94    SetDigitalTimer {
95        day_of_month: operand::DayOfMonth,
96        month_of_year: operand::MonthOfYear,
97        start_time: operand::Time,
98        duration: operand::Duration,
99        recording_sequence: operand::RecordingSequence,
100        service_id: operand::DigitalServiceId,
101    } = constants::CEC_MSG_SET_DIGITAL_TIMER,
102    SetExtTimer {
103        day_of_month: operand::DayOfMonth,
104        month_of_year: operand::MonthOfYear,
105        start_time: operand::Time,
106        duration: operand::Duration,
107        recording_sequence: operand::RecordingSequence,
108        external_source: operand::ExternalSource,
109    } = constants::CEC_MSG_SET_EXT_TIMER,
110    SetTimerProgramTitle {
111        title: operand::BufferOperand,
112    } = constants::CEC_MSG_SET_TIMER_PROGRAM_TITLE,
113    TimerClearedStatus {
114        status: operand::TimerClearedStatusData,
115    } = constants::CEC_MSG_TIMER_CLEARED_STATUS,
116    TimerStatus {
117        status: operand::TimerStatusData,
118    } = constants::CEC_MSG_TIMER_STATUS,
119    CecVersion {
120        version: operand::Version,
121    } = constants::CEC_MSG_CEC_VERSION,
122    GetCecVersion = constants::CEC_MSG_GET_CEC_VERSION,
123    GivePhysicalAddr = constants::CEC_MSG_GIVE_PHYSICAL_ADDR,
124    GetMenuLanguage = constants::CEC_MSG_GET_MENU_LANGUAGE,
125    #[addressing = "broadcast"]
126    ReportPhysicalAddr {
127        physical_address: PhysicalAddress,
128        device_type: operand::PrimaryDeviceType,
129    } = constants::CEC_MSG_REPORT_PHYSICAL_ADDR,
130    #[addressing = "broadcast"]
131    SetMenuLanguage {
132        language: [u8; 3],
133    } = constants::CEC_MSG_SET_MENU_LANGUAGE,
134    DeckControl {
135        mode: operand::DeckControlMode,
136    } = constants::CEC_MSG_DECK_CONTROL,
137    DeckStatus {
138        info: operand::DeckInfo,
139    } = constants::CEC_MSG_DECK_STATUS,
140    GiveDeckStatus {
141        request: operand::StatusRequest,
142    } = constants::CEC_MSG_GIVE_DECK_STATUS,
143    Play {
144        mode: operand::PlayMode,
145    } = constants::CEC_MSG_PLAY,
146    GiveTunerDeviceStatus {
147        request: operand::StatusRequest,
148    } = constants::CEC_MSG_GIVE_TUNER_DEVICE_STATUS,
149    SelectAnalogueService {
150        service_id: operand::AnalogueServiceId,
151    } = constants::CEC_MSG_SELECT_ANALOGUE_SERVICE,
152    SelectDigitalService {
153        service_id: operand::DigitalServiceId,
154    } = constants::CEC_MSG_SELECT_DIGITAL_SERVICE,
155    TunerDeviceStatus {
156        info: operand::TunerDeviceInfo,
157    } = constants::CEC_MSG_TUNER_DEVICE_STATUS,
158    TunerStepDecrement = constants::CEC_MSG_TUNER_STEP_DECREMENT,
159    TunerStepIncrement = constants::CEC_MSG_TUNER_STEP_INCREMENT,
160    #[addressing = "broadcast"]
161    DeviceVendorId {
162        vendor_id: crate::VendorId,
163    } = constants::CEC_MSG_DEVICE_VENDOR_ID,
164    GiveDeviceVendorId = constants::CEC_MSG_GIVE_DEVICE_VENDOR_ID,
165    VendorCommand {
166        command: operand::BufferOperand,
167    } = constants::CEC_MSG_VENDOR_COMMAND,
168    #[addressing = "either"]
169    VendorCommandWithId {
170        vendor_id: crate::VendorId,
171        vendor_specific_data: operand::BoundedBufferOperand<11, u8>,
172    } = constants::CEC_MSG_VENDOR_COMMAND_WITH_ID,
173    #[addressing = "either"]
174    VendorRemoteButtonDown {
175        rc_code: operand::BufferOperand,
176    } = constants::CEC_MSG_VENDOR_REMOTE_BUTTON_DOWN,
177    #[addressing = "either"]
178    VendorRemoteButtonUp = constants::CEC_MSG_VENDOR_REMOTE_BUTTON_UP,
179    SetOsdString {
180        display_control: operand::DisplayControl,
181        osd_string: operand::BoundedBufferOperand<13, u8>,
182    } = constants::CEC_MSG_SET_OSD_STRING,
183    GiveOsdName = constants::CEC_MSG_GIVE_OSD_NAME,
184    SetOsdName {
185        name: operand::BufferOperand,
186    } = constants::CEC_MSG_SET_OSD_NAME,
187    MenuRequest {
188        request_type: operand::MenuRequestType,
189    } = constants::CEC_MSG_MENU_REQUEST,
190    MenuStatus {
191        state: operand::MenuState,
192    } = constants::CEC_MSG_MENU_STATUS,
193    UserControlPressed {
194        ui_command: operand::UiCommand,
195    } = constants::CEC_MSG_USER_CONTROL_PRESSED,
196    UserControlReleased = constants::CEC_MSG_USER_CONTROL_RELEASED,
197    GiveDevicePowerStatus = constants::CEC_MSG_GIVE_DEVICE_POWER_STATUS,
198    #[addressing = "either"]
199    ReportPowerStatus {
200        status: operand::PowerStatus,
201    } = constants::CEC_MSG_REPORT_POWER_STATUS,
202    FeatureAbort {
203        opcode: u8,
204        abort_reason: operand::AbortReason,
205    } = constants::CEC_MSG_FEATURE_ABORT,
206    Abort = constants::CEC_MSG_ABORT,
207    GiveAudioStatus = constants::CEC_MSG_GIVE_AUDIO_STATUS,
208    GiveSystemAudioModeStatus = constants::CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS,
209    ReportAudioStatus {
210        status: operand::AudioStatus,
211    } = constants::CEC_MSG_REPORT_AUDIO_STATUS,
212    ReportShortAudioDescriptor {
213        descriptors: operand::BoundedBufferOperand<4, operand::ShortAudioDescriptor>,
214    } = constants::CEC_MSG_REPORT_SHORT_AUDIO_DESCRIPTOR,
215    RequestShortAudioDescriptor {
216        descriptors: operand::BoundedBufferOperand<4, operand::AudioFormatIdAndCode>,
217    } = constants::CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR,
218    #[addressing = "either"]
219    SetSystemAudioMode {
220        status: bool,
221    } = constants::CEC_MSG_SET_SYSTEM_AUDIO_MODE,
222    SystemAudioModeRequest {
223        physical_address: PhysicalAddress,
224    } = constants::CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST,
225    SystemAudioModeStatus {
226        status: bool,
227    } = constants::CEC_MSG_SYSTEM_AUDIO_MODE_STATUS,
228    SetAudioRate {
229        audio_rate: operand::AudioRate,
230    } = constants::CEC_MSG_SET_AUDIO_RATE,
231    /* HDMI 1.4b */
232    InitiateArc = constants::CEC_MSG_INITIATE_ARC,
233    ReportArcInitiated = constants::CEC_MSG_REPORT_ARC_INITIATED,
234    ReportArcTerminated = constants::CEC_MSG_REPORT_ARC_TERMINATED,
235    RequestArcInitiation = constants::CEC_MSG_REQUEST_ARC_INITIATION,
236    RequestArcTermination = constants::CEC_MSG_REQUEST_ARC_TERMINATION,
237    TerminateArc = constants::CEC_MSG_TERMINATE_ARC,
238    #[addressing = "broadcast"]
239    CdcMessage {
240        initiator: PhysicalAddress,
241        message: cdc::Message,
242    } = constants::CEC_MSG_CDC_MESSAGE,
243    /* HDMI 2.0 */
244    #[addressing = "broadcast"]
245    ReportFeatures {
246        version: operand::Version,
247        device_types: operand::AllDeviceTypes,
248        rc_profile: operand::RcProfile,
249        device_features: operand::DeviceFeatures,
250    } = constants::CEC_MSG_REPORT_FEATURES,
251    GiveFeatures = constants::CEC_MSG_GIVE_FEATURES,
252    #[addressing = "broadcast"]
253    RequestCurrentLatency {
254        physical_address: PhysicalAddress,
255    } = constants::CEC_MSG_REQUEST_CURRENT_LATENCY,
256    #[addressing = "broadcast"]
257    ReportCurrentLatency {
258        physical_address: PhysicalAddress,
259        video_latency: operand::Delay,
260        flags: operand::LatencyFlags,
261        audio_output_delay: Option<operand::Delay>,
262    } = constants::CEC_MSG_REPORT_CURRENT_LATENCY,
263    /* HDMI 2.1a */
264    SetAudioVolumeLevel {
265        volume_level: operand::AudioVolumeLevel,
266    } = constants::CEC_MSG_SET_AUDIO_VOLUME_LEVEL,
267}
268
269impl Message {
270    #[must_use]
271    pub fn opcode(&self) -> Opcode {
272        let opcode = unsafe { *<*const _>::from(self).cast::<u8>() };
273        Opcode::try_from_primitive(opcode).unwrap()
274    }
275
276    /// Report whether or not this `Message` can be directly
277    /// addressed to a specific logical address.
278    #[must_use]
279    pub fn can_directly_address(&self) -> bool {
280        self.opcode().can_directly_address()
281    }
282
283    /// Report whether or not this `Message` can be
284    /// broadcast to all logical addresses.
285    #[must_use]
286    pub fn can_broadcast(&self) -> bool {
287        self.opcode().can_broadcast()
288    }
289
290    /// Get the [`AddressingType`] that reports whether this `Message` can be
291    /// addressed directly to a specific logical address, broadcast to all
292    /// logical addresses, or both.
293    #[must_use]
294    pub fn addressing_type(&self) -> AddressingType {
295        self.opcode().addressing_type()
296    }
297}
298
299impl Opcode {
300    #[must_use]
301    pub fn can_directly_address(&self) -> bool {
302        matches!(
303            self.addressing_type(),
304            AddressingType::Direct | AddressingType::Either
305        )
306    }
307
308    #[must_use]
309    pub fn can_broadcast(&self) -> bool {
310        matches!(
311            self.addressing_type(),
312            AddressingType::Broadcast | AddressingType::Either
313        )
314    }
315}
316
317#[cfg(test)]
318mod test_active_source {
319    use super::*;
320
321    message_test! {
322        ty: ActiveSource,
323        instance: Message::ActiveSource {
324            address: PhysicalAddress(0x1234),
325        },
326        bytes: [0x12, 0x34],
327        extra: [Overfull, Empty],
328    }
329
330    #[test]
331    fn test_decoding_missing_byte() {
332        assert_eq!(
333            Message::try_from_bytes(&[Opcode::ActiveSource as u8, 0x12]),
334            Err(Error::OutOfRange {
335                expected: Range::AtLeast(3),
336                got: 2,
337                quantity: "bytes",
338            })
339        );
340    }
341}
342
343#[cfg(test)]
344mod test_inactive_source {
345    use super::*;
346
347    message_test! {
348        ty: InactiveSource,
349        instance: Message::InactiveSource {
350            address: PhysicalAddress(0x1234),
351        },
352        bytes: [0x12, 0x34],
353        extra: [Overfull, Empty],
354    }
355
356    #[test]
357    fn test_decoding_missing_byte() {
358        assert_eq!(
359            Message::try_from_bytes(&[Opcode::InactiveSource as u8, 0x12]),
360            Err(Error::OutOfRange {
361                expected: Range::AtLeast(3),
362                got: 2,
363                quantity: "bytes",
364            })
365        );
366    }
367}
368
369#[cfg(test)]
370mod test_routing_change {
371    use super::*;
372
373    message_test! {
374        ty: RoutingChange,
375        instance: Message::RoutingChange {
376            original_address: PhysicalAddress(0x1234),
377            new_address: PhysicalAddress(0x5678),
378        },
379        bytes: [0x12, 0x34, 0x56, 0x78],
380        extra: [Overfull, Empty],
381    }
382
383    #[test]
384    fn test_decoding_missing_byte() {
385        assert_eq!(
386            Message::try_from_bytes(&[Opcode::RoutingChange as u8, 0x12, 0x34, 0x56]),
387            Err(Error::OutOfRange {
388                expected: Range::AtLeast(5),
389                got: 4,
390                quantity: "bytes",
391            })
392        );
393    }
394
395    #[test]
396    fn test_decoding_missing_operand() {
397        assert_eq!(
398            Message::try_from_bytes(&[Opcode::RoutingChange as u8, 0x12, 0x34]),
399            Err(Error::OutOfRange {
400                expected: Range::AtLeast(5),
401                got: 3,
402                quantity: "bytes",
403            })
404        );
405    }
406
407    #[test]
408    fn test_decoding_missing_operand_and_byte() {
409        assert_eq!(
410            Message::try_from_bytes(&[Opcode::RoutingChange as u8, 0x12]),
411            Err(Error::OutOfRange {
412                expected: Range::AtLeast(5),
413                got: 2,
414                quantity: "bytes",
415            })
416        );
417    }
418}
419
420#[cfg(test)]
421mod test_routing_information {
422    use super::*;
423
424    message_test! {
425        ty: RoutingInformation,
426        instance: Message::RoutingInformation {
427            address: PhysicalAddress(0x1234),
428        },
429        bytes: [0x12, 0x34],
430        extra: [Overfull, Empty],
431    }
432
433    #[test]
434    fn test_decoding_missing_byte() {
435        assert_eq!(
436            Message::try_from_bytes(&[Opcode::RoutingInformation as u8, 0x12]),
437            Err(Error::OutOfRange {
438                expected: Range::AtLeast(3),
439                got: 2,
440                quantity: "bytes",
441            })
442        );
443    }
444}
445
446#[cfg(test)]
447mod test_set_stream_path {
448    use super::*;
449
450    message_test! {
451        ty: SetStreamPath,
452        instance: Message::SetStreamPath {
453            address: PhysicalAddress(0x1234),
454        },
455        bytes: [0x12, 0x34],
456        extra: [Overfull, Empty],
457    }
458
459    #[test]
460    fn test_decoding_missing_byte() {
461        assert_eq!(
462            Message::try_from_bytes(&[Opcode::SetStreamPath as u8, 0x12]),
463            Err(Error::OutOfRange {
464                expected: Range::AtLeast(3),
465                got: 2,
466                quantity: "bytes",
467            })
468        );
469    }
470}
471
472#[cfg(test)]
473mod test_record_on {
474    use super::*;
475
476    message_test! {
477        name: _own,
478        ty: RecordOn,
479        instance: Message::RecordOn {
480            source: operand::RecordSource::Own,
481        },
482        bytes: [operand::RecordSourceType::Own as u8],
483        extra: [Overfull],
484    }
485
486    message_test! {
487        name: _digital,
488        ty: RecordOn,
489        instance: Message::RecordOn {
490            source: operand::RecordSource::DigitalService(
491                operand::DigitalServiceId::AribGeneric(operand::AribData {
492                    transport_stream_id: 0x1234,
493                    service_id: 0x5678,
494                    original_network_id: 0x9ABC,
495                })
496            )
497        },
498        bytes: [
499            operand::RecordSourceType::Digital as u8,
500            operand::DigitalServiceBroadcastSystem::AribGeneric as u8,
501            0x12,
502            0x34,
503            0x56,
504            0x78,
505            0x9A,
506            0xBC
507        ],
508        extra: [Overfull],
509    }
510
511    message_test! {
512        name: _analogue,
513        ty: RecordOn,
514        instance: Message::RecordOn {
515            source: operand::RecordSource::AnalogueService(operand::AnalogueServiceId {
516                broadcast_type: operand::AnalogueBroadcastType::Satellite,
517                frequency: 0x1234,
518                broadcast_system: operand::BroadcastSystem::SecamL,
519            })
520        },
521        bytes: [
522            operand::RecordSourceType::Analogue as u8,
523            operand::AnalogueBroadcastType::Satellite as u8,
524            0x12,
525            0x34,
526            operand::BroadcastSystem::SecamL as u8
527        ],
528        extra: [Overfull],
529    }
530
531    #[test]
532    fn test_decode_missing_operands() {
533        assert_eq!(
534            Message::try_from_bytes(&[Opcode::RecordOn as u8]),
535            Err(Error::OutOfRange {
536                expected: Range::AtLeast(2),
537                got: 1,
538                quantity: "bytes",
539            })
540        );
541    }
542
543    #[test]
544    fn test_opcode() {
545        assert_eq!(
546            Message::RecordOn {
547                source: operand::RecordSource::Own
548            }
549            .opcode(),
550            Opcode::RecordOn
551        );
552    }
553}
554
555#[cfg(test)]
556mod test_record_status {
557    use super::*;
558
559    message_test! {
560        ty: RecordStatus,
561        instance: Message::RecordStatus {
562            status: operand::RecordStatusInfo::CurrentSource
563        },
564        bytes: [operand::RecordStatusInfo::CurrentSource as u8],
565        extra: [Overfull, Empty],
566    }
567
568    #[test]
569    fn test_decode_invalid_operand() {
570        assert_eq!(
571            Message::try_from_bytes(&[Opcode::RecordStatus as u8, 0xFE]),
572            Err(Error::InvalidValueForType {
573                ty: "RecordStatusInfo",
574                value: String::from("254"),
575            })
576        );
577    }
578}
579
580#[cfg(test)]
581mod test_clear_analogue_timer {
582    use super::*;
583
584    message_test! {
585        ty: ClearAnalogueTimer,
586        instance: Message::ClearAnalogueTimer {
587            day_of_month: operand::DayOfMonth::Day1,
588            month_of_year: operand::MonthOfYear::January,
589            start_time: operand::Time {
590                hour: operand::Hour::try_from(12).unwrap(),
591                minute: operand::Minute::try_from(30).unwrap(),
592            },
593            duration: operand::Duration {
594                hours: operand::DurationHours::try_from(99).unwrap(),
595                minutes: operand::Minute::try_from(59).unwrap(),
596            },
597            recording_sequence: operand::RecordingSequence::SUNDAY,
598            service_id: operand::AnalogueServiceId {
599                broadcast_type: operand::AnalogueBroadcastType::Terrestrial,
600                frequency: 0x1234,
601                broadcast_system: operand::BroadcastSystem::NtscM
602            },
603        },
604        bytes: [
605            operand::DayOfMonth::Day1 as u8,
606            operand::MonthOfYear::January as u8,
607            0x12,
608            0x30,
609            0x99,
610            0x59,
611            constants::CEC_OP_REC_SEQ_SUNDAY,
612            operand::AnalogueBroadcastType::Terrestrial as u8,
613            0x12,
614            0x34,
615            operand::BroadcastSystem::NtscM as u8
616        ],
617        extra: [Overfull, Empty],
618    }
619
620    #[test]
621    fn test_decoding_missing_operand_1() {
622        assert_eq!(
623            Message::try_from_bytes(&[
624                Opcode::ClearAnalogueTimer as u8,
625                operand::DayOfMonth::Day1 as u8,
626                operand::MonthOfYear::January as u8,
627                0x12,
628                0x30,
629                0x99,
630                0x59,
631                constants::CEC_OP_REC_SEQ_SUNDAY,
632            ]),
633            Err(Error::OutOfRange {
634                expected: Range::AtLeast(12),
635                got: 8,
636                quantity: "bytes",
637            })
638        );
639    }
640
641    #[test]
642    fn test_decoding_missing_operand_2() {
643        assert_eq!(
644            Message::try_from_bytes(&[
645                Opcode::ClearAnalogueTimer as u8,
646                operand::DayOfMonth::Day1 as u8,
647                operand::MonthOfYear::January as u8,
648                0x12,
649                0x30,
650                0x99,
651                0x59,
652            ]),
653            Err(Error::OutOfRange {
654                expected: Range::AtLeast(12),
655                got: 7,
656                quantity: "bytes",
657            })
658        );
659    }
660
661    #[test]
662    fn test_decoding_missing_operand_3() {
663        assert_eq!(
664            Message::try_from_bytes(&[
665                Opcode::ClearAnalogueTimer as u8,
666                operand::DayOfMonth::Day1 as u8,
667                operand::MonthOfYear::January as u8,
668                0x12,
669                0x30,
670            ]),
671            Err(Error::OutOfRange {
672                expected: Range::AtLeast(12),
673                got: 5,
674                quantity: "bytes",
675            })
676        );
677    }
678
679    #[test]
680    fn test_decoding_missing_operand_4() {
681        assert_eq!(
682            Message::try_from_bytes(&[
683                Opcode::ClearAnalogueTimer as u8,
684                operand::DayOfMonth::Day1 as u8,
685                operand::MonthOfYear::January as u8,
686            ]),
687            Err(Error::OutOfRange {
688                expected: Range::AtLeast(12),
689                got: 3,
690                quantity: "bytes",
691            })
692        );
693    }
694
695    #[test]
696    fn test_decoding_missing_operand_5() {
697        assert_eq!(
698            Message::try_from_bytes(&[
699                Opcode::ClearAnalogueTimer as u8,
700                operand::DayOfMonth::Day1 as u8,
701            ]),
702            Err(Error::OutOfRange {
703                expected: Range::AtLeast(12),
704                got: 2,
705                quantity: "bytes",
706            })
707        );
708    }
709}
710
711#[cfg(test)]
712mod test_clear_digital_timer {
713    use super::*;
714
715    message_test! {
716        ty: ClearDigitalTimer,
717        instance: Message::ClearDigitalTimer {
718            day_of_month: operand::DayOfMonth::Day1,
719            month_of_year: operand::MonthOfYear::January,
720            start_time: operand::Time {
721                hour: operand::Hour::try_from(12).unwrap(),
722                minute: operand::Minute::try_from(30).unwrap(),
723            },
724            duration: operand::Duration {
725                hours: operand::DurationHours::try_from(99).unwrap(),
726                minutes: operand::Minute::try_from(59).unwrap(),
727            },
728            recording_sequence: operand::RecordingSequence::SUNDAY,
729            service_id: operand::DigitalServiceId::AtscCable(
730                operand::AtscData {
731                    transport_stream_id: 0x1234,
732                    program_number: 0x5678,
733                }
734            ),
735        },
736        bytes: [
737            operand::DayOfMonth::Day1 as u8,
738            operand::MonthOfYear::January as u8,
739            0x12,
740            0x30,
741            0x99,
742            0x59,
743            constants::CEC_OP_REC_SEQ_SUNDAY,
744            operand::DigitalServiceBroadcastSystem::AtscCable as u8,
745            0x12,
746            0x34,
747            0x56,
748            0x78,
749            0,
750            0,
751        ],
752        extra: [Empty],
753    }
754
755    #[test]
756    fn test_decoding_missing_operand_1() {
757        assert_eq!(
758            Message::try_from_bytes(&[
759                Opcode::ClearDigitalTimer as u8,
760                operand::DayOfMonth::Day1 as u8,
761                operand::MonthOfYear::January as u8,
762                0x12,
763                0x30,
764                0x99,
765                0x59,
766                constants::CEC_OP_REC_SEQ_SUNDAY,
767            ]),
768            Err(Error::OutOfRange {
769                expected: Range::AtLeast(15),
770                got: 8,
771                quantity: "bytes",
772            })
773        );
774    }
775
776    #[test]
777    fn test_decoding_missing_operand_2() {
778        assert_eq!(
779            Message::try_from_bytes(&[
780                Opcode::ClearDigitalTimer as u8,
781                operand::DayOfMonth::Day1 as u8,
782                operand::MonthOfYear::January as u8,
783                0x12,
784                0x30,
785                0x99,
786                0x59,
787            ]),
788            Err(Error::OutOfRange {
789                expected: Range::AtLeast(15),
790                got: 7,
791                quantity: "bytes",
792            })
793        );
794    }
795
796    #[test]
797    fn test_decoding_missing_operand_3() {
798        assert_eq!(
799            Message::try_from_bytes(&[
800                Opcode::ClearDigitalTimer as u8,
801                operand::DayOfMonth::Day1 as u8,
802                operand::MonthOfYear::January as u8,
803                0x12,
804                0x30,
805            ]),
806            Err(Error::OutOfRange {
807                expected: Range::AtLeast(15),
808                got: 5,
809                quantity: "bytes",
810            })
811        );
812    }
813
814    #[test]
815    fn test_decoding_missing_operand_4() {
816        assert_eq!(
817            Message::try_from_bytes(&[
818                Opcode::ClearDigitalTimer as u8,
819                operand::DayOfMonth::Day1 as u8,
820                operand::MonthOfYear::January as u8,
821            ]),
822            Err(Error::OutOfRange {
823                expected: Range::AtLeast(15),
824                got: 3,
825                quantity: "bytes",
826            })
827        );
828    }
829
830    #[test]
831    fn test_decoding_missing_operand_5() {
832        assert_eq!(
833            Message::try_from_bytes(&[
834                Opcode::ClearDigitalTimer as u8,
835                operand::DayOfMonth::Day1 as u8,
836            ]),
837            Err(Error::OutOfRange {
838                expected: Range::AtLeast(15),
839                got: 2,
840                quantity: "bytes",
841            })
842        );
843    }
844}
845
846#[cfg(test)]
847mod test_clear_ext_timer {
848    use super::*;
849
850    message_test! {
851        name: _phys_addr,
852        ty: ClearExtTimer,
853        instance: Message::ClearExtTimer {
854            day_of_month: operand::DayOfMonth::Day1,
855            month_of_year: operand::MonthOfYear::January,
856            start_time: operand::Time {
857                hour: operand::Hour::try_from(12).unwrap(),
858                minute: operand::Minute::try_from(30).unwrap(),
859            },
860            duration: operand::Duration {
861                hours: operand::DurationHours::try_from(99).unwrap(),
862                minutes: operand::Minute::try_from(59).unwrap(),
863            },
864            recording_sequence: operand::RecordingSequence::SUNDAY,
865            external_source: operand::ExternalSource::PhysicalAddress(PhysicalAddress(0x1234)),
866        },
867        bytes: [
868            operand::DayOfMonth::Day1 as u8,
869            operand::MonthOfYear::January as u8,
870            0x12,
871            0x30,
872            0x99,
873            0x59,
874            constants::CEC_OP_REC_SEQ_SUNDAY,
875            0x12,
876            0x34,
877        ],
878    }
879
880    message_test! {
881        name: _plug,
882        ty: ClearExtTimer,
883        instance: Message::ClearExtTimer {
884            day_of_month: operand::DayOfMonth::Day1,
885            month_of_year: operand::MonthOfYear::January,
886            start_time: operand::Time {
887                hour: operand::Hour::try_from(12).unwrap(),
888                minute: operand::Minute::try_from(30).unwrap(),
889            },
890            duration: operand::Duration {
891                hours: operand::DurationHours::try_from(99).unwrap(),
892                minutes: operand::Minute::try_from(59).unwrap(),
893            },
894            recording_sequence: operand::RecordingSequence::SUNDAY,
895            external_source: operand::ExternalSource::Plug(0x56),
896        },
897        bytes: [
898            operand::DayOfMonth::Day1 as u8,
899            operand::MonthOfYear::January as u8,
900            0x12,
901            0x30,
902            0x99,
903            0x59,
904            constants::CEC_OP_REC_SEQ_SUNDAY,
905            0x56,
906        ],
907    }
908
909    #[test]
910    fn test_opcode() {
911        assert_eq!(
912            Message::ClearExtTimer {
913                day_of_month: operand::DayOfMonth::Day1,
914                month_of_year: operand::MonthOfYear::January,
915                start_time: operand::Time {
916                    hour: operand::Hour::try_from(12).unwrap(),
917                    minute: operand::Minute::try_from(30).unwrap(),
918                },
919                duration: operand::Duration {
920                    hours: operand::DurationHours::try_from(99).unwrap(),
921                    minutes: operand::Minute::try_from(59).unwrap(),
922                },
923                recording_sequence: operand::RecordingSequence::SUNDAY,
924                external_source: operand::ExternalSource::Plug(0x56),
925            }
926            .opcode(),
927            Opcode::ClearExtTimer
928        );
929    }
930
931    #[test]
932    fn test_decoding_missing_operand_1() {
933        assert_eq!(
934            Message::try_from_bytes(&[
935                Opcode::ClearExtTimer as u8,
936                operand::DayOfMonth::Day1 as u8,
937                operand::MonthOfYear::January as u8,
938                0x12,
939                0x30,
940                0x99,
941                0x59,
942                constants::CEC_OP_REC_SEQ_SUNDAY,
943            ]),
944            Err(Error::OutOfRange {
945                expected: Range::Only(vec![9, 10]),
946                got: 8,
947                quantity: "bytes",
948            })
949        );
950    }
951
952    #[test]
953    fn test_decoding_missing_operand_2() {
954        assert_eq!(
955            Message::try_from_bytes(&[
956                Opcode::ClearExtTimer as u8,
957                operand::DayOfMonth::Day1 as u8,
958                operand::MonthOfYear::January as u8,
959                0x12,
960                0x30,
961                0x99,
962                0x59,
963            ]),
964            Err(Error::OutOfRange {
965                expected: Range::Only(vec![9, 10]),
966                got: 7,
967                quantity: "bytes",
968            })
969        );
970    }
971
972    #[test]
973    fn test_decoding_missing_operand_3() {
974        assert_eq!(
975            Message::try_from_bytes(&[
976                Opcode::ClearExtTimer as u8,
977                operand::DayOfMonth::Day1 as u8,
978                operand::MonthOfYear::January as u8,
979                0x12,
980                0x30,
981            ]),
982            Err(Error::OutOfRange {
983                expected: Range::Only(vec![9, 10]),
984                got: 5,
985                quantity: "bytes",
986            })
987        );
988    }
989
990    #[test]
991    fn test_decoding_missing_operand_4() {
992        assert_eq!(
993            Message::try_from_bytes(&[
994                Opcode::ClearExtTimer as u8,
995                operand::DayOfMonth::Day1 as u8,
996                operand::MonthOfYear::January as u8,
997            ]),
998            Err(Error::OutOfRange {
999                expected: Range::Only(vec![9, 10]),
1000                got: 3,
1001                quantity: "bytes",
1002            })
1003        );
1004    }
1005
1006    #[test]
1007    fn test_decoding_missing_operand_5() {
1008        assert_eq!(
1009            Message::try_from_bytes(&[
1010                Opcode::ClearExtTimer as u8,
1011                operand::DayOfMonth::Day1 as u8,
1012            ]),
1013            Err(Error::OutOfRange {
1014                expected: Range::Only(vec![9, 10]),
1015                got: 2,
1016                quantity: "bytes",
1017            })
1018        );
1019    }
1020
1021    #[test]
1022    fn test_decoding_missing_operand_6() {
1023        assert_eq!(
1024            Message::try_from_bytes(&[Opcode::ClearExtTimer as u8]),
1025            Err(Error::OutOfRange {
1026                expected: Range::Only(vec![9, 10]),
1027                got: 1,
1028                quantity: "bytes",
1029            })
1030        );
1031    }
1032}
1033
1034#[cfg(test)]
1035mod test_set_analogue_timer {
1036    use super::*;
1037
1038    message_test! {
1039        ty: SetAnalogueTimer,
1040        instance: Message::SetAnalogueTimer {
1041            day_of_month: operand::DayOfMonth::Day1,
1042            month_of_year: operand::MonthOfYear::January,
1043            start_time: operand::Time {
1044                hour: operand::Hour::try_from(12).unwrap(),
1045                minute: operand::Minute::try_from(30).unwrap(),
1046            },
1047            duration: operand::Duration {
1048                hours: operand::DurationHours::try_from(99).unwrap(),
1049                minutes: operand::Minute::try_from(59).unwrap(),
1050            },
1051            recording_sequence: operand::RecordingSequence::SUNDAY,
1052            service_id: operand::AnalogueServiceId {
1053                broadcast_type: operand::AnalogueBroadcastType::Terrestrial,
1054                frequency: 0x1234,
1055                broadcast_system: operand::BroadcastSystem::NtscM
1056            },
1057        },
1058        bytes: [
1059            operand::DayOfMonth::Day1 as u8,
1060            operand::MonthOfYear::January as u8,
1061            0x12,
1062            0x30,
1063            0x99,
1064            0x59,
1065            constants::CEC_OP_REC_SEQ_SUNDAY,
1066            operand::AnalogueBroadcastType::Terrestrial as u8,
1067            0x12,
1068            0x34,
1069            operand::BroadcastSystem::NtscM as u8
1070        ],
1071        extra: [Overfull, Empty],
1072    }
1073
1074    #[test]
1075    fn test_decoding_missing_operand_1() {
1076        assert_eq!(
1077            Message::try_from_bytes(&[
1078                Opcode::SetAnalogueTimer as u8,
1079                operand::DayOfMonth::Day1 as u8,
1080                operand::MonthOfYear::January as u8,
1081                0x12,
1082                0x30,
1083                0x99,
1084                0x59,
1085                constants::CEC_OP_REC_SEQ_SUNDAY,
1086            ]),
1087            Err(Error::OutOfRange {
1088                expected: Range::AtLeast(12),
1089                got: 8,
1090                quantity: "bytes",
1091            })
1092        );
1093    }
1094
1095    #[test]
1096    fn test_decoding_missing_operand_2() {
1097        assert_eq!(
1098            Message::try_from_bytes(&[
1099                Opcode::SetAnalogueTimer as u8,
1100                operand::DayOfMonth::Day1 as u8,
1101                operand::MonthOfYear::January as u8,
1102                0x12,
1103                0x30,
1104                0x99,
1105                0x59,
1106            ]),
1107            Err(Error::OutOfRange {
1108                expected: Range::AtLeast(12),
1109                got: 7,
1110                quantity: "bytes",
1111            })
1112        );
1113    }
1114
1115    #[test]
1116    fn test_decoding_missing_operand_3() {
1117        assert_eq!(
1118            Message::try_from_bytes(&[
1119                Opcode::SetAnalogueTimer as u8,
1120                operand::DayOfMonth::Day1 as u8,
1121                operand::MonthOfYear::January as u8,
1122                0x12,
1123                0x30,
1124            ]),
1125            Err(Error::OutOfRange {
1126                expected: Range::AtLeast(12),
1127                got: 5,
1128                quantity: "bytes",
1129            })
1130        );
1131    }
1132
1133    #[test]
1134    fn test_decoding_missing_operand_4() {
1135        assert_eq!(
1136            Message::try_from_bytes(&[
1137                Opcode::SetAnalogueTimer as u8,
1138                operand::DayOfMonth::Day1 as u8,
1139                operand::MonthOfYear::January as u8,
1140            ]),
1141            Err(Error::OutOfRange {
1142                expected: Range::AtLeast(12),
1143                got: 3,
1144                quantity: "bytes",
1145            })
1146        );
1147    }
1148
1149    #[test]
1150    fn test_decoding_missing_operand_5() {
1151        assert_eq!(
1152            Message::try_from_bytes(&[
1153                Opcode::SetAnalogueTimer as u8,
1154                operand::DayOfMonth::Day1 as u8,
1155            ]),
1156            Err(Error::OutOfRange {
1157                expected: Range::AtLeast(12),
1158                got: 2,
1159                quantity: "bytes",
1160            })
1161        );
1162    }
1163}
1164
1165#[cfg(test)]
1166mod test_set_digital_timer {
1167    use super::*;
1168
1169    message_test! {
1170        ty: SetDigitalTimer,
1171        instance: Message::SetDigitalTimer {
1172            day_of_month: operand::DayOfMonth::Day1,
1173            month_of_year: operand::MonthOfYear::January,
1174            start_time: operand::Time {
1175                hour: operand::Hour::try_from(12).unwrap(),
1176                minute: operand::Minute::try_from(30).unwrap(),
1177            },
1178            duration: operand::Duration {
1179                hours: operand::DurationHours::try_from(99).unwrap(),
1180                minutes: operand::Minute::try_from(59).unwrap(),
1181            },
1182            recording_sequence: operand::RecordingSequence::SUNDAY,
1183            service_id: operand::DigitalServiceId::AtscCable(
1184                operand::AtscData {
1185                    transport_stream_id: 0x1234,
1186                    program_number: 0x5678,
1187                }
1188            ),
1189        },
1190        bytes: [
1191            operand::DayOfMonth::Day1 as u8,
1192            operand::MonthOfYear::January as u8,
1193            0x12,
1194            0x30,
1195            0x99,
1196            0x59,
1197            constants::CEC_OP_REC_SEQ_SUNDAY,
1198            operand::DigitalServiceBroadcastSystem::AtscCable as u8,
1199            0x12,
1200            0x34,
1201            0x56,
1202            0x78,
1203            0,
1204            0,
1205        ],
1206        extra: [Empty],
1207    }
1208
1209    #[test]
1210    fn test_decoding_missing_operand_1() {
1211        assert_eq!(
1212            Message::try_from_bytes(&[
1213                Opcode::SetDigitalTimer as u8,
1214                operand::DayOfMonth::Day1 as u8,
1215                operand::MonthOfYear::January as u8,
1216                0x12,
1217                0x30,
1218                0x99,
1219                0x59,
1220                constants::CEC_OP_REC_SEQ_SUNDAY,
1221            ]),
1222            Err(Error::OutOfRange {
1223                expected: Range::AtLeast(15),
1224                got: 8,
1225                quantity: "bytes",
1226            })
1227        );
1228    }
1229
1230    #[test]
1231    fn test_decoding_missing_operand_2() {
1232        assert_eq!(
1233            Message::try_from_bytes(&[
1234                Opcode::SetDigitalTimer as u8,
1235                operand::DayOfMonth::Day1 as u8,
1236                operand::MonthOfYear::January as u8,
1237                0x12,
1238                0x30,
1239                0x99,
1240                0x59,
1241            ]),
1242            Err(Error::OutOfRange {
1243                expected: Range::AtLeast(15),
1244                got: 7,
1245                quantity: "bytes",
1246            })
1247        );
1248    }
1249
1250    #[test]
1251    fn test_decoding_missing_operand_3() {
1252        assert_eq!(
1253            Message::try_from_bytes(&[
1254                Opcode::SetDigitalTimer as u8,
1255                operand::DayOfMonth::Day1 as u8,
1256                operand::MonthOfYear::January as u8,
1257                0x12,
1258                0x30,
1259            ]),
1260            Err(Error::OutOfRange {
1261                expected: Range::AtLeast(15),
1262                got: 5,
1263                quantity: "bytes",
1264            })
1265        );
1266    }
1267
1268    #[test]
1269    fn test_decoding_missing_operand_4() {
1270        assert_eq!(
1271            Message::try_from_bytes(&[
1272                Opcode::SetDigitalTimer as u8,
1273                operand::DayOfMonth::Day1 as u8,
1274                operand::MonthOfYear::January as u8,
1275            ]),
1276            Err(Error::OutOfRange {
1277                expected: Range::AtLeast(15),
1278                got: 3,
1279                quantity: "bytes",
1280            })
1281        );
1282    }
1283
1284    #[test]
1285    fn test_decoding_missing_operand_5() {
1286        assert_eq!(
1287            Message::try_from_bytes(&[
1288                Opcode::SetDigitalTimer as u8,
1289                operand::DayOfMonth::Day1 as u8,
1290            ]),
1291            Err(Error::OutOfRange {
1292                expected: Range::AtLeast(15),
1293                got: 2,
1294                quantity: "bytes",
1295            })
1296        );
1297    }
1298
1299    #[test]
1300    fn test_decoding_missing_operand_6() {
1301        assert_eq!(
1302            Message::try_from_bytes(&[Opcode::SetDigitalTimer as u8]),
1303            Err(Error::OutOfRange {
1304                expected: Range::AtLeast(15),
1305                got: 1,
1306                quantity: "bytes",
1307            })
1308        );
1309    }
1310}
1311
1312#[cfg(test)]
1313mod test_set_ext_timer {
1314    use super::*;
1315
1316    message_test! {
1317        name: _phys_addr,
1318        ty: SetExtTimer,
1319        instance: Message::SetExtTimer {
1320            day_of_month: operand::DayOfMonth::Day1,
1321            month_of_year: operand::MonthOfYear::January,
1322            start_time: operand::Time {
1323                hour: operand::Hour::try_from(12).unwrap(),
1324                minute: operand::Minute::try_from(30).unwrap(),
1325            },
1326            duration: operand::Duration {
1327                hours: operand::DurationHours::try_from(99).unwrap(),
1328                minutes: operand::Minute::try_from(59).unwrap(),
1329            },
1330            recording_sequence: operand::RecordingSequence::SUNDAY,
1331            external_source: operand::ExternalSource::PhysicalAddress(PhysicalAddress(0x1234)),
1332        },
1333        bytes: [
1334            operand::DayOfMonth::Day1 as u8,
1335            operand::MonthOfYear::January as u8,
1336            0x12,
1337            0x30,
1338            0x99,
1339            0x59,
1340            constants::CEC_OP_REC_SEQ_SUNDAY,
1341            0x12,
1342            0x34,
1343        ],
1344    }
1345
1346    message_test! {
1347        name: _plug,
1348        ty: SetExtTimer,
1349        instance: Message::SetExtTimer {
1350            day_of_month: operand::DayOfMonth::Day1,
1351            month_of_year: operand::MonthOfYear::January,
1352            start_time: operand::Time {
1353                hour: operand::Hour::try_from(12).unwrap(),
1354                minute: operand::Minute::try_from(30).unwrap(),
1355            },
1356            duration: operand::Duration {
1357                hours: operand::DurationHours::try_from(99).unwrap(),
1358                minutes: operand::Minute::try_from(59).unwrap(),
1359            },
1360            recording_sequence: operand::RecordingSequence::SUNDAY,
1361            external_source: operand::ExternalSource::Plug(0x56),
1362        },
1363        bytes: [
1364            operand::DayOfMonth::Day1 as u8,
1365            operand::MonthOfYear::January as u8,
1366            0x12,
1367            0x30,
1368            0x99,
1369            0x59,
1370            constants::CEC_OP_REC_SEQ_SUNDAY,
1371            0x56,
1372        ],
1373    }
1374
1375    #[test]
1376    fn test_opcode() {
1377        assert_eq!(
1378            Message::SetExtTimer {
1379                day_of_month: operand::DayOfMonth::Day1,
1380                month_of_year: operand::MonthOfYear::January,
1381                start_time: operand::Time {
1382                    hour: operand::Hour::try_from(12).unwrap(),
1383                    minute: operand::Minute::try_from(30).unwrap(),
1384                },
1385                duration: operand::Duration {
1386                    hours: operand::DurationHours::try_from(99).unwrap(),
1387                    minutes: operand::Minute::try_from(59).unwrap(),
1388                },
1389                recording_sequence: operand::RecordingSequence::SUNDAY,
1390                external_source: operand::ExternalSource::Plug(0x56),
1391            }
1392            .opcode(),
1393            Opcode::SetExtTimer
1394        );
1395    }
1396
1397    #[test]
1398    fn test_decoding_missing_operand_1() {
1399        assert_eq!(
1400            Message::try_from_bytes(&[
1401                Opcode::SetExtTimer as u8,
1402                operand::DayOfMonth::Day1 as u8,
1403                operand::MonthOfYear::January as u8,
1404                0x12,
1405                0x30,
1406                0x99,
1407                0x59,
1408                constants::CEC_OP_REC_SEQ_SUNDAY,
1409            ]),
1410            Err(Error::OutOfRange {
1411                expected: Range::Only(vec![9, 10]),
1412                got: 8,
1413                quantity: "bytes",
1414            })
1415        );
1416    }
1417
1418    #[test]
1419    fn test_decoding_missing_operand_2() {
1420        assert_eq!(
1421            Message::try_from_bytes(&[
1422                Opcode::SetExtTimer as u8,
1423                operand::DayOfMonth::Day1 as u8,
1424                operand::MonthOfYear::January as u8,
1425                0x12,
1426                0x30,
1427                0x99,
1428                0x59,
1429            ]),
1430            Err(Error::OutOfRange {
1431                expected: Range::Only(vec![9, 10]),
1432                got: 7,
1433                quantity: "bytes",
1434            })
1435        );
1436    }
1437
1438    #[test]
1439    fn test_decoding_missing_operand_3() {
1440        assert_eq!(
1441            Message::try_from_bytes(&[
1442                Opcode::SetExtTimer as u8,
1443                operand::DayOfMonth::Day1 as u8,
1444                operand::MonthOfYear::January as u8,
1445                0x12,
1446                0x30,
1447            ]),
1448            Err(Error::OutOfRange {
1449                expected: Range::Only(vec![9, 10]),
1450                got: 5,
1451                quantity: "bytes",
1452            })
1453        );
1454    }
1455
1456    #[test]
1457    fn test_decoding_missing_operand_4() {
1458        assert_eq!(
1459            Message::try_from_bytes(&[
1460                Opcode::SetExtTimer as u8,
1461                operand::DayOfMonth::Day1 as u8,
1462                operand::MonthOfYear::January as u8,
1463            ]),
1464            Err(Error::OutOfRange {
1465                expected: Range::Only(vec![9, 10]),
1466                got: 3,
1467                quantity: "bytes",
1468            })
1469        );
1470    }
1471
1472    #[test]
1473    fn test_decoding_missing_operand_5() {
1474        assert_eq!(
1475            Message::try_from_bytes(&[Opcode::SetExtTimer as u8, operand::DayOfMonth::Day1 as u8]),
1476            Err(Error::OutOfRange {
1477                expected: Range::Only(vec![9, 10]),
1478                got: 2,
1479                quantity: "bytes",
1480            })
1481        );
1482    }
1483
1484    #[test]
1485    fn test_decoding_missing_operand_6() {
1486        assert_eq!(
1487            Message::try_from_bytes(&[Opcode::SetExtTimer as u8]),
1488            Err(Error::OutOfRange {
1489                expected: Range::Only(vec![9, 10]),
1490                got: 1,
1491                quantity: "bytes",
1492            })
1493        );
1494    }
1495}
1496
1497#[cfg(test)]
1498mod test_set_timer_program_title {
1499    use super::*;
1500
1501    message_test! {
1502        name: _empty,
1503        ty: SetTimerProgramTitle,
1504        instance: Message::SetTimerProgramTitle {
1505            title: operand::BufferOperand::from_str("").unwrap(),
1506        },
1507        bytes: [],
1508    }
1509
1510    message_test! {
1511        name: _full,
1512        ty: SetTimerProgramTitle,
1513        instance: Message::SetTimerProgramTitle {
1514            title: operand::BufferOperand::from_str("12345678901234").unwrap(),
1515        },
1516        bytes: [
1517            b'1',
1518            b'2',
1519            b'3',
1520            b'4',
1521            b'5',
1522            b'6',
1523            b'7',
1524            b'8',
1525            b'9',
1526            b'0',
1527            b'1',
1528            b'2',
1529            b'3',
1530            b'4'
1531        ],
1532    }
1533
1534    #[test]
1535    fn test_opcode() {
1536        assert_eq!(
1537            Message::SetTimerProgramTitle {
1538                title: operand::BufferOperand::from_str("12345678901234").unwrap(),
1539            }
1540            .opcode(),
1541            Opcode::SetTimerProgramTitle
1542        );
1543    }
1544}
1545
1546#[cfg(test)]
1547mod test_timer_cleared_status {
1548    use super::*;
1549
1550    message_test! {
1551        ty: TimerClearedStatus,
1552        instance: Message::TimerClearedStatus {
1553            status: operand::TimerClearedStatusData::Cleared,
1554        },
1555        bytes: [operand::TimerClearedStatusData::Cleared as u8],
1556        extra: [Overfull, Empty],
1557    }
1558}
1559
1560#[cfg(test)]
1561mod test_timer_status {
1562    use super::*;
1563
1564    message_test! {
1565        ty: TimerStatus,
1566        instance: Message::TimerStatus {
1567            status: operand::TimerStatusData {
1568                overlap_warning: false,
1569                media_info: operand::MediaInfo::UnprotectedMedia,
1570                programmed_info: operand::TimerProgrammedInfo::Programmed(operand::ProgrammedInfo::EnoughSpace),
1571            },
1572        },
1573        bytes: [(operand::MediaInfo::UnprotectedMedia as u8) | 0x10 | constants::CEC_OP_PROG_INFO_ENOUGH_SPACE],
1574        extra: [Overfull, Empty],
1575    }
1576
1577    message_test! {
1578        name: _no_duration,
1579        ty: TimerStatus,
1580        instance: Message::TimerStatus {
1581            status: operand::TimerStatusData {
1582                overlap_warning: false,
1583                media_info: operand::MediaInfo::UnprotectedMedia,
1584                programmed_info: operand::TimerProgrammedInfo::Programmed(operand::ProgrammedInfo::NotEnoughSpace {
1585                    duration_available: None,
1586                }),
1587            },
1588        },
1589        bytes: [(operand::MediaInfo::UnprotectedMedia as u8) | 0x10 | constants::CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE],
1590    }
1591
1592    message_test! {
1593        name: _duration,
1594        ty: TimerStatus,
1595        instance: Message::TimerStatus {
1596            status: operand::TimerStatusData {
1597                overlap_warning: false,
1598                media_info: operand::MediaInfo::UnprotectedMedia,
1599                programmed_info: operand::TimerProgrammedInfo::Programmed(operand::ProgrammedInfo::NotEnoughSpace {
1600                    duration_available: Some(operand::Duration {
1601                        hours: operand::DurationHours::try_from(30).unwrap(),
1602                        minutes: operand::Minute::try_from(45).unwrap(),
1603                    }),
1604                }),
1605            },
1606        },
1607        bytes: [
1608            (operand::MediaInfo::UnprotectedMedia as u8) | 0x10 | constants::CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE,
1609            0x30,
1610            0x45
1611        ],
1612        extra: [Overfull],
1613    }
1614}
1615
1616#[cfg(test)]
1617mod test_cec_version {
1618    use super::*;
1619
1620    message_test! {
1621        ty: CecVersion,
1622        instance: Message::CecVersion {
1623            version: operand::Version::V2_0,
1624        },
1625        bytes: [operand::Version::V2_0 as u8],
1626        extra: [Overfull, Empty],
1627    }
1628}
1629
1630#[cfg(test)]
1631mod test_report_physical_addr {
1632    use super::*;
1633
1634    message_test! {
1635        ty: ReportPhysicalAddr,
1636        instance: Message::ReportPhysicalAddr {
1637            physical_address: PhysicalAddress(0x1234),
1638            device_type: operand::PrimaryDeviceType::Processor,
1639        },
1640        bytes: [0x12, 0x34, operand::PrimaryDeviceType::Processor as u8],
1641        extra: [Overfull, Empty],
1642    }
1643
1644    #[test]
1645    fn test_decoding_missing_operand() {
1646        assert_eq!(
1647            Message::try_from_bytes(&[Opcode::ReportPhysicalAddr as u8, 0x12, 0x34]),
1648            Err(Error::OutOfRange {
1649                expected: Range::AtLeast(4),
1650                got: 3,
1651                quantity: "bytes",
1652            })
1653        );
1654    }
1655
1656    #[test]
1657    fn test_decoding_missing_operand_and_byte() {
1658        assert_eq!(
1659            Message::try_from_bytes(&[Opcode::ReportPhysicalAddr as u8, 0x12]),
1660            Err(Error::OutOfRange {
1661                expected: Range::AtLeast(4),
1662                got: 2,
1663                quantity: "bytes",
1664            })
1665        );
1666    }
1667}
1668
1669#[cfg(test)]
1670mod test_set_menu_language {
1671    use super::*;
1672
1673    message_test! {
1674        ty: SetMenuLanguage,
1675        instance: Message::SetMenuLanguage {
1676            language: [0x12, 0x34, 0x56],
1677        },
1678        bytes: [0x12, 0x34, 0x56],
1679        extra: [Overfull, Empty],
1680    }
1681
1682    #[test]
1683    fn test_decoding_missing_byte() {
1684        assert_eq!(
1685            Message::try_from_bytes(&[Opcode::SetMenuLanguage as u8, 0x12, 0x34]),
1686            Err(Error::OutOfRange {
1687                expected: Range::AtLeast(4),
1688                got: 3,
1689                quantity: "bytes",
1690            })
1691        );
1692    }
1693
1694    #[test]
1695    fn test_decoding_missing_bytes() {
1696        assert_eq!(
1697            Message::try_from_bytes(&[Opcode::SetMenuLanguage as u8, 0x12]),
1698            Err(Error::OutOfRange {
1699                expected: Range::AtLeast(4),
1700                got: 2,
1701                quantity: "bytes",
1702            })
1703        );
1704    }
1705}
1706
1707#[cfg(test)]
1708mod test_deck_control {
1709    use super::*;
1710
1711    message_test! {
1712        ty: DeckControl,
1713        instance: Message::DeckControl {
1714            mode: operand::DeckControlMode::Stop,
1715        },
1716        bytes: [operand::DeckControlMode::Stop as u8],
1717        extra: [Overfull, Empty],
1718    }
1719}
1720
1721#[cfg(test)]
1722mod test_deck_status {
1723    use super::*;
1724
1725    message_test! {
1726        ty: DeckStatus,
1727        instance: Message::DeckStatus {
1728            info: operand::DeckInfo::Record,
1729        },
1730        bytes: [operand::DeckInfo::Record as u8],
1731        extra: [Overfull, Empty],
1732    }
1733}
1734
1735#[cfg(test)]
1736mod test_give_deck_status {
1737    use super::*;
1738
1739    message_test! {
1740        ty: GiveDeckStatus,
1741        instance: Message::GiveDeckStatus {
1742            request: operand::StatusRequest::Once,
1743        },
1744        bytes: [operand::StatusRequest::Once as u8],
1745        extra: [Overfull, Empty],
1746    }
1747}
1748
1749#[cfg(test)]
1750mod test_play {
1751    use super::*;
1752
1753    message_test! {
1754        ty: Play,
1755        instance: Message::Play {
1756            mode: operand::PlayMode::Still,
1757        },
1758        bytes: [operand::PlayMode::Still as u8],
1759        extra: [Overfull, Empty],
1760    }
1761}
1762
1763#[cfg(test)]
1764mod test_give_tuner_device_status {
1765    use super::*;
1766
1767    message_test! {
1768        ty: GiveTunerDeviceStatus,
1769        instance: Message::GiveTunerDeviceStatus {
1770            request: operand::StatusRequest::Once,
1771        },
1772        bytes: [operand::StatusRequest::Once as u8],
1773        extra: [Overfull, Empty],
1774    }
1775}
1776
1777#[cfg(test)]
1778mod test_select_analogue_service {
1779    use super::*;
1780
1781    message_test! {
1782        ty: SelectAnalogueService,
1783        instance: Message::SelectAnalogueService {
1784            service_id: operand::AnalogueServiceId {
1785                broadcast_type: operand::AnalogueBroadcastType::Terrestrial,
1786                frequency: 0x1234,
1787                broadcast_system: operand::BroadcastSystem::PalBG,
1788            },
1789        },
1790        bytes: [
1791            operand::AnalogueBroadcastType::Terrestrial as u8,
1792            0x12,
1793            0x34,
1794            operand::BroadcastSystem::PalBG as u8
1795        ],
1796        extra: [Overfull, Empty],
1797    }
1798}
1799
1800#[cfg(test)]
1801mod test_select_digital_service {
1802    use super::*;
1803
1804    message_test! {
1805        ty: SelectDigitalService,
1806        instance: Message::SelectDigitalService {
1807            service_id: operand::DigitalServiceId::AribGeneric(operand::AribData {
1808                transport_stream_id: 0x1234,
1809                service_id: 0x5678,
1810                original_network_id: 0xABCD,
1811            }),
1812        },
1813        bytes: [
1814            operand::DigitalServiceBroadcastSystem::AribGeneric as u8,
1815            0x12,
1816            0x34,
1817            0x56,
1818            0x78,
1819            0xAB,
1820            0xCD,
1821        ],
1822        extra: [Overfull, Empty],
1823    }
1824}
1825
1826#[cfg(test)]
1827mod test_tuner_device_status {
1828    use super::*;
1829
1830    message_test! {
1831        ty: TunerDeviceStatus,
1832        instance: Message::TunerDeviceStatus {
1833            info: operand::TunerDeviceInfo {
1834                recording: true,
1835                tuner_display_info: operand::TunerDisplayInfo::Analogue,
1836                service_id: operand::ServiceId::Analogue(operand::AnalogueServiceId {
1837                    broadcast_type: operand::AnalogueBroadcastType::Terrestrial,
1838                    frequency: 0x1234,
1839                    broadcast_system: operand::BroadcastSystem::PalBG,
1840                }),
1841            },
1842        },
1843        bytes: [
1844            0x82,
1845            operand::AnalogueBroadcastType::Terrestrial as u8,
1846            0x12,
1847            0x34,
1848            operand::BroadcastSystem::PalBG as u8
1849        ],
1850        extra: [Empty],
1851    }
1852}
1853
1854#[cfg(test)]
1855mod test_device_vendor_id {
1856    use super::*;
1857
1858    message_test! {
1859        ty: DeviceVendorId,
1860        instance: Message::DeviceVendorId {
1861            vendor_id: crate::VendorId([0x12, 0x34, 0x56]),
1862        },
1863        bytes: [0x12, 0x34, 0x56],
1864        extra: [Overfull, Empty],
1865    }
1866}
1867
1868#[cfg(test)]
1869mod test_vendor_command {
1870    use super::*;
1871
1872    message_test! {
1873        name: _empty,
1874        ty: VendorCommand,
1875        instance: Message::VendorCommand {
1876            command: operand::BufferOperand::from_str("").unwrap(),
1877        },
1878        bytes: [],
1879    }
1880
1881    message_test! {
1882        name: _full,
1883        ty: VendorCommand,
1884        instance: Message::VendorCommand {
1885            command: operand::BufferOperand::from_str("12345678901234").unwrap(),
1886        },
1887        bytes: [
1888            b'1',
1889            b'2',
1890            b'3',
1891            b'4',
1892            b'5',
1893            b'6',
1894            b'7',
1895            b'8',
1896            b'9',
1897            b'0',
1898            b'1',
1899            b'2',
1900            b'3',
1901            b'4'
1902        ],
1903    }
1904
1905    #[test]
1906    fn test_opcode() {
1907        assert_eq!(
1908            Message::VendorCommand {
1909                command: operand::BufferOperand::from_str("12345678901234").unwrap(),
1910            }
1911            .opcode(),
1912            Opcode::VendorCommand
1913        );
1914    }
1915}
1916
1917#[cfg(test)]
1918mod test_vendor_command_with_id {
1919    use super::*;
1920
1921    message_test! {
1922        name: _empty,
1923        ty: VendorCommandWithId,
1924        instance: Message::VendorCommandWithId {
1925            vendor_id: crate::VendorId([0x12, 0x34, 0x56]),
1926            vendor_specific_data: operand::BoundedBufferOperand::<11, u8>::from_str("").unwrap(),
1927        },
1928        bytes: [0x12, 0x34, 0x56],
1929    }
1930
1931    message_test! {
1932        name: _full,
1933        ty: VendorCommandWithId,
1934        instance: Message::VendorCommandWithId {
1935            vendor_id: crate::VendorId([0x12, 0x34, 0x56]),
1936            vendor_specific_data: operand::BoundedBufferOperand::<11, u8>::from_str("12345678901").unwrap(),
1937        },
1938        bytes: [
1939            0x12,
1940            0x34,
1941            0x56,
1942            b'1',
1943            b'2',
1944            b'3',
1945            b'4',
1946            b'5',
1947            b'6',
1948            b'7',
1949            b'8',
1950            b'9',
1951            b'0',
1952            b'1',
1953        ],
1954    }
1955
1956    #[test]
1957    fn test_opcode() {
1958        assert_eq!(
1959            Message::VendorCommandWithId {
1960                vendor_id: crate::VendorId([0x12, 0x34, 0x56]),
1961                vendor_specific_data: operand::BoundedBufferOperand::<11, u8>::from_str("")
1962                    .unwrap(),
1963            }
1964            .opcode(),
1965            Opcode::VendorCommandWithId
1966        );
1967    }
1968
1969    #[test]
1970    fn test_decoding_missing_operand() {
1971        assert_eq!(
1972            Message::try_from_bytes(&[Opcode::VendorCommandWithId as u8]),
1973            Err(Error::OutOfRange {
1974                expected: Range::AtLeast(4),
1975                got: 1,
1976                quantity: "bytes",
1977            })
1978        );
1979    }
1980}
1981
1982#[cfg(test)]
1983mod test_vendor_remote_button_down {
1984    use super::*;
1985
1986    message_test! {
1987        name: _empty,
1988        ty: VendorRemoteButtonDown,
1989        instance: Message::VendorRemoteButtonDown {
1990            rc_code: operand::BufferOperand::from_str("").unwrap(),
1991        },
1992        bytes: [],
1993    }
1994
1995    message_test! {
1996        name: _full,
1997        ty: VendorRemoteButtonDown,
1998        instance: Message::VendorRemoteButtonDown {
1999            rc_code: operand::BufferOperand::from_str("12345678901234").unwrap(),
2000        },
2001        bytes: [
2002            b'1',
2003            b'2',
2004            b'3',
2005            b'4',
2006            b'5',
2007            b'6',
2008            b'7',
2009            b'8',
2010            b'9',
2011            b'0',
2012            b'1',
2013            b'2',
2014            b'3',
2015            b'4'
2016        ],
2017    }
2018
2019    #[test]
2020    fn test_opcode() {
2021        assert_eq!(
2022            Message::VendorRemoteButtonDown {
2023                rc_code: operand::BufferOperand::from_str("12345678901234").unwrap(),
2024            }
2025            .opcode(),
2026            Opcode::VendorRemoteButtonDown
2027        );
2028    }
2029}
2030
2031#[cfg(test)]
2032mod test_set_osd_string {
2033    use super::*;
2034
2035    message_test! {
2036        name: _empty,
2037        ty: SetOsdString,
2038        instance: Message::SetOsdString {
2039            display_control: operand::DisplayControl::UntilCleared,
2040            osd_string: operand::BoundedBufferOperand::<13, u8>::from_str("").unwrap(),
2041        },
2042        bytes: [operand::DisplayControl::UntilCleared as u8],
2043    }
2044
2045    message_test! {
2046        name: _full,
2047        ty: SetOsdString,
2048        instance: Message::SetOsdString {
2049            display_control: operand::DisplayControl::UntilCleared,
2050            osd_string: operand::BoundedBufferOperand::<13, u8>::from_str("1234567890123").unwrap(),
2051        },
2052        bytes: [
2053            operand::DisplayControl::UntilCleared as u8,
2054            b'1',
2055            b'2',
2056            b'3',
2057            b'4',
2058            b'5',
2059            b'6',
2060            b'7',
2061            b'8',
2062            b'9',
2063            b'0',
2064            b'1',
2065            b'2',
2066            b'3',
2067        ],
2068    }
2069
2070    #[test]
2071    fn test_opcode() {
2072        assert_eq!(
2073            Message::SetOsdString {
2074                display_control: operand::DisplayControl::UntilCleared,
2075                osd_string: operand::BoundedBufferOperand::<13, u8>::from_str("").unwrap(),
2076            }
2077            .opcode(),
2078            Opcode::SetOsdString
2079        );
2080    }
2081
2082    #[test]
2083    fn test_decoding_missing_operand() {
2084        assert_eq!(
2085            Message::try_from_bytes(&[Opcode::SetOsdString as u8]),
2086            Err(Error::OutOfRange {
2087                expected: Range::AtLeast(2),
2088                got: 1,
2089                quantity: "bytes",
2090            })
2091        );
2092    }
2093}
2094
2095#[cfg(test)]
2096mod test_set_osd_name {
2097    use super::*;
2098
2099    message_test! {
2100        name: _empty,
2101        ty: SetOsdName,
2102        instance: Message::SetOsdName {
2103            name: operand::BufferOperand::from_str("").unwrap(),
2104        },
2105        bytes: [],
2106    }
2107
2108    message_test! {
2109        name: _full,
2110        ty: SetOsdName,
2111        instance: Message::SetOsdName {
2112            name: operand::BufferOperand::from_str("12345678901234").unwrap(),
2113        },
2114        bytes: [
2115            b'1',
2116            b'2',
2117            b'3',
2118            b'4',
2119            b'5',
2120            b'6',
2121            b'7',
2122            b'8',
2123            b'9',
2124            b'0',
2125            b'1',
2126            b'2',
2127            b'3',
2128            b'4'
2129        ],
2130    }
2131
2132    #[test]
2133    fn test_opcode() {
2134        assert_eq!(
2135            Message::SetOsdName {
2136                name: operand::BufferOperand::from_str("12345678901234").unwrap(),
2137            }
2138            .opcode(),
2139            Opcode::SetOsdName
2140        );
2141    }
2142}
2143
2144#[cfg(test)]
2145mod test_menu_request {
2146    use super::*;
2147
2148    message_test! {
2149        ty: MenuRequest,
2150        instance: Message::MenuRequest {
2151            request_type: operand::MenuRequestType::Query,
2152        },
2153        bytes: [operand::MenuRequestType::Query as u8],
2154        extra: [Overfull, Empty],
2155    }
2156}
2157
2158#[cfg(test)]
2159mod test_menu_state {
2160    use super::*;
2161
2162    message_test! {
2163        ty: MenuStatus,
2164        instance: Message::MenuStatus {
2165            state: operand::MenuState::Deactivated,
2166        },
2167        bytes: [operand::MenuState::Deactivated as u8],
2168        extra: [Overfull, Empty],
2169    }
2170}
2171
2172#[cfg(test)]
2173mod test_user_control_pressed {
2174    use super::*;
2175
2176    message_test! {
2177        ty: UserControlPressed,
2178        instance: Message::UserControlPressed {
2179            ui_command: operand::UiCommand::Play,
2180        },
2181        bytes: [constants::CEC_OP_UI_CMD_PLAY as u8],
2182        extra: [Overfull, Empty],
2183    }
2184}
2185
2186#[cfg(test)]
2187mod test_report_power_status {
2188    use super::*;
2189
2190    message_test! {
2191        ty: ReportPowerStatus,
2192        instance: Message::ReportPowerStatus {
2193            status: operand::PowerStatus::ToOn,
2194        },
2195        bytes: [operand::PowerStatus::ToOn as u8],
2196        extra: [Overfull, Empty],
2197    }
2198}
2199
2200#[cfg(test)]
2201mod test_feature_abort {
2202    use super::*;
2203
2204    message_test! {
2205        ty: FeatureAbort,
2206        instance: Message::FeatureAbort {
2207            opcode: Opcode::FeatureAbort.into(),
2208            abort_reason: operand::AbortReason::IncorrectMode,
2209        },
2210        bytes: [Opcode::FeatureAbort as u8, operand::AbortReason::IncorrectMode as u8],
2211        extra: [Overfull, Empty],
2212    }
2213
2214    #[test]
2215    fn test_decoding_missing_operand() {
2216        assert_eq!(
2217            Message::try_from_bytes(&[Opcode::FeatureAbort as u8, Opcode::FeatureAbort as u8]),
2218            Err(Error::OutOfRange {
2219                expected: Range::AtLeast(3),
2220                got: 2,
2221                quantity: "bytes",
2222            })
2223        );
2224    }
2225}
2226
2227#[cfg(test)]
2228mod test_report_audio_status {
2229    use super::*;
2230
2231    message_test! {
2232        ty: ReportAudioStatus,
2233        instance: Message::ReportAudioStatus {
2234            status: operand::AudioStatus::new().with_volume(2).with_mute(true),
2235        },
2236        bytes: [0x82],
2237        extra: [Overfull, Empty],
2238    }
2239}
2240
2241#[cfg(test)]
2242mod test_report_short_audio_descriptor {
2243    use super::*;
2244
2245    message_test! {
2246        name: _empty,
2247        ty: ReportShortAudioDescriptor,
2248        instance: Message::ReportShortAudioDescriptor {
2249            descriptors: operand::BoundedBufferOperand::default(),
2250        },
2251        bytes: [],
2252    }
2253
2254    message_test! {
2255        name: _full,
2256        ty: ReportShortAudioDescriptor,
2257        instance: Message::ReportShortAudioDescriptor {
2258            descriptors: operand::BoundedBufferOperand::try_from([
2259                [0x01, 0x23, 0x45],
2260                [0x67, 0x89, 0xAB],
2261                [0xCD, 0xEF, 0xFE],
2262                [0xDC, 0xBA, 0x98]
2263            ].as_ref())
2264            .unwrap(),
2265        },
2266        bytes: [
2267            0x01,
2268            0x23,
2269            0x45,
2270            0x67,
2271            0x89,
2272            0xAB,
2273            0xCD,
2274            0xEF,
2275            0xFE,
2276            0xDC,
2277            0xBA,
2278            0x98,
2279        ],
2280    }
2281
2282    #[test]
2283    fn test_opcode() {
2284        assert_eq!(
2285            Message::ReportShortAudioDescriptor {
2286                descriptors: operand::BoundedBufferOperand::try_from(
2287                    [
2288                        [0x01, 0x23, 0x45],
2289                        [0x67, 0x89, 0xAB],
2290                        [0xCD, 0xEF, 0xFE],
2291                        [0xDC, 0xBA, 0x98]
2292                    ]
2293                    .as_ref()
2294                )
2295                .unwrap(),
2296            }
2297            .opcode(),
2298            Opcode::ReportShortAudioDescriptor
2299        );
2300    }
2301}
2302
2303#[cfg(test)]
2304mod test_request_short_audio_descriptor {
2305    use super::*;
2306
2307    message_test! {
2308        name: _empty,
2309        ty: RequestShortAudioDescriptor,
2310        instance: Message::RequestShortAudioDescriptor {
2311            descriptors: operand::BoundedBufferOperand::default(),
2312        },
2313        bytes: [],
2314    }
2315
2316    message_test! {
2317        name: _full,
2318        ty: RequestShortAudioDescriptor,
2319        instance: Message::RequestShortAudioDescriptor {
2320            descriptors: operand::BoundedBufferOperand::try_from([
2321                operand::AudioFormatIdAndCode::new()
2322                    .with_code(1)
2323                    .with_id(operand::AudioFormatId::CEA861),
2324                operand::AudioFormatIdAndCode::new()
2325                    .with_code(2)
2326                    .with_id(operand::AudioFormatId::CEA861),
2327                operand::AudioFormatIdAndCode::new()
2328                    .with_code(3)
2329                    .with_id(operand::AudioFormatId::CEA861Cxt),
2330                operand::AudioFormatIdAndCode::new()
2331                    .with_code(4)
2332                    .with_id(operand::AudioFormatId::CEA861Cxt),
2333            ].as_ref())
2334            .unwrap(),
2335        },
2336        bytes: [0x01, 0x02, 0x43, 0x44],
2337    }
2338
2339    #[test]
2340    fn test_opcode() {
2341        assert_eq!(
2342            Message::RequestShortAudioDescriptor {
2343                descriptors: operand::BoundedBufferOperand::try_from(
2344                    [
2345                        operand::AudioFormatIdAndCode::new()
2346                            .with_code(1)
2347                            .with_id(operand::AudioFormatId::CEA861),
2348                        operand::AudioFormatIdAndCode::new()
2349                            .with_code(2)
2350                            .with_id(operand::AudioFormatId::CEA861),
2351                        operand::AudioFormatIdAndCode::new()
2352                            .with_code(3)
2353                            .with_id(operand::AudioFormatId::CEA861Cxt),
2354                        operand::AudioFormatIdAndCode::new()
2355                            .with_code(4)
2356                            .with_id(operand::AudioFormatId::CEA861Cxt),
2357                    ]
2358                    .as_ref()
2359                )
2360                .unwrap(),
2361            }
2362            .opcode(),
2363            Opcode::RequestShortAudioDescriptor
2364        );
2365    }
2366}
2367
2368#[cfg(test)]
2369mod test_set_system_audio_mode {
2370    use super::*;
2371
2372    message_test! {
2373        ty: SetSystemAudioMode,
2374        instance: Message::SetSystemAudioMode {
2375            status: true,
2376        },
2377        bytes: [0x01],
2378        extra: [Overfull, Empty],
2379    }
2380}
2381
2382#[cfg(test)]
2383mod test_system_audio_mode_request {
2384    use super::*;
2385
2386    message_test! {
2387        ty: SystemAudioModeRequest,
2388        instance: Message::SystemAudioModeRequest {
2389            physical_address: PhysicalAddress(0x1234),
2390        },
2391        bytes: [0x12, 0x34],
2392        extra: [Overfull, Empty],
2393    }
2394
2395    #[test]
2396    fn test_decoding_missing_byte() {
2397        assert_eq!(
2398            Message::try_from_bytes(&[Opcode::SystemAudioModeRequest as u8, 0x12]),
2399            Err(Error::OutOfRange {
2400                expected: Range::AtLeast(3),
2401                got: 2,
2402                quantity: "bytes",
2403            })
2404        );
2405    }
2406}
2407
2408#[cfg(test)]
2409mod test_system_audio_mode_status {
2410    use super::*;
2411
2412    message_test! {
2413        ty: SystemAudioModeStatus,
2414        instance: Message::SystemAudioModeStatus {
2415            status: true,
2416        },
2417        bytes: [0x01],
2418        extra: [Overfull, Empty],
2419    }
2420}
2421
2422#[cfg(test)]
2423mod test_set_audio_rate {
2424    use super::*;
2425
2426    message_test! {
2427        ty: SetAudioRate,
2428        instance: Message::SetAudioRate {
2429            audio_rate: operand::AudioRate::WideFast,
2430        },
2431        bytes: [operand::AudioRate::WideFast as u8],
2432        extra: [Overfull, Empty],
2433    }
2434}
2435
2436#[cfg(test)]
2437mod test_cdc_message {
2438    use super::*;
2439
2440    message_test! {
2441        ty: CdcMessage,
2442        instance: Message::CdcMessage {
2443            initiator: PhysicalAddress(0x0123),
2444            message: CdcMessage::HecRequestDeactivation {
2445                terminating_address1: PhysicalAddress(0x4567),
2446                terminating_address2: PhysicalAddress(0x89AB),
2447                terminating_address3: PhysicalAddress(0xCDEF)
2448            },
2449        },
2450        bytes: [0x01, 0x23, CdcOpcode::HecRequestDeactivation as u8, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF],
2451        extra: [Overfull, Empty],
2452    }
2453
2454    #[test]
2455    fn test_decoding_missing_operand() {
2456        assert_eq!(
2457            Message::try_from_bytes(&[
2458                Opcode::CdcMessage as u8,
2459                0x01,
2460                0x23,
2461                CdcOpcode::HecRequestDeactivation as u8
2462            ]),
2463            Err(Error::OutOfRange {
2464                expected: Range::AtLeast(10),
2465                got: 4,
2466                quantity: "bytes",
2467            })
2468        );
2469    }
2470
2471    #[test]
2472    fn test_decoding_missing_opcode() {
2473        assert_eq!(
2474            Message::try_from_bytes(&[Opcode::CdcMessage as u8, 0x01, 0x23]),
2475            Err(Error::OutOfRange {
2476                expected: Range::AtLeast(4),
2477                got: 3,
2478                quantity: "bytes",
2479            })
2480        );
2481    }
2482
2483    #[test]
2484    fn test_decoding_missing_opcode_and_byte() {
2485        assert_eq!(
2486            Message::try_from_bytes(&[Opcode::CdcMessage as u8, 0x01]),
2487            Err(Error::OutOfRange {
2488                expected: Range::AtLeast(4),
2489                got: 2,
2490                quantity: "bytes",
2491            })
2492        );
2493    }
2494}
2495
2496#[cfg(test)]
2497mod test_report_features {
2498    use super::*;
2499
2500    message_test! {
2501        name: _empty,
2502        ty: ReportFeatures,
2503        instance: Message::ReportFeatures {
2504            version: operand::Version::V2_0,
2505            device_types: operand::AllDeviceTypes::PLAYBACK,
2506            rc_profile: operand::RcProfile::new(
2507                operand::RcProfile1::Source(operand::RcProfileSource::all())),
2508            device_features: operand::DeviceFeatures::new(operand::DeviceFeatures1::all()),
2509        },
2510        bytes: [
2511            operand::Version::V2_0 as u8,
2512            operand::AllDeviceTypes::PLAYBACK.bits(),
2513            operand::RcProfileSource::all().bits(),
2514            operand::DeviceFeatures1::all().bits()
2515        ],
2516        extra: [Overfull],
2517    }
2518
2519    message_test! {
2520        name: _extra_rc_profiles,
2521        ty: ReportFeatures,
2522        instance: Message::ReportFeatures {
2523            version: operand::Version::V2_0,
2524            device_types: operand::AllDeviceTypes::PLAYBACK,
2525            rc_profile: operand::RcProfile {
2526                rc_profile_1: operand::RcProfile1::Source(operand::RcProfileSource::all()),
2527                rc_profile_n: operand::BoundedBufferOperand::try_from([
2528                    0,
2529                    0,
2530                    0,
2531                    0,
2532                    0,
2533                    0,
2534                    0,
2535                    0,
2536                    0
2537                ].as_ref()).unwrap(),
2538            },
2539            device_features: operand::DeviceFeatures::new(operand::DeviceFeatures1::all()),
2540        },
2541        bytes: [
2542            operand::Version::V2_0 as u8,
2543            operand::AllDeviceTypes::PLAYBACK.bits(),
2544            operand::RcProfileSource::all().bits() | 0x80,
2545            0x80,
2546            0x80,
2547            0x80,
2548            0x80,
2549            0x80,
2550            0x80,
2551            0x80,
2552            0x80,
2553            0x0,
2554            operand::DeviceFeatures1::all().bits()
2555        ],
2556    }
2557
2558    message_test! {
2559        name: _extra_device_features,
2560        ty: ReportFeatures,
2561        instance: Message::ReportFeatures {
2562            version: operand::Version::V2_0,
2563            device_types: operand::AllDeviceTypes::PLAYBACK,
2564            rc_profile: operand::RcProfile::new(operand::RcProfile1::Source(operand::RcProfileSource::all())),
2565            device_features: operand::DeviceFeatures {
2566                device_features_1: operand::DeviceFeatures1::all(),
2567                device_features_n: operand::BoundedBufferOperand::try_from([
2568                    0,
2569                    0,
2570                    0,
2571                    0,
2572                    0,
2573                    0,
2574                    0,
2575                    0,
2576                    0
2577                ].as_ref()).unwrap(),
2578            },
2579        },
2580        bytes: [
2581            operand::Version::V2_0 as u8,
2582            operand::AllDeviceTypes::PLAYBACK.bits(),
2583            operand::RcProfileSource::all().bits(),
2584            operand::DeviceFeatures1::all().bits() | 0x80,
2585            0x80,
2586            0x80,
2587            0x80,
2588            0x80,
2589            0x80,
2590            0x80,
2591            0x80,
2592            0x80,
2593            0x0,
2594        ],
2595    }
2596
2597    message_test! {
2598        name: _balanced,
2599        ty: ReportFeatures,
2600        instance: Message::ReportFeatures {
2601            version: operand::Version::V2_0,
2602            device_types: operand::AllDeviceTypes::PLAYBACK,
2603            rc_profile: operand::RcProfile {
2604                rc_profile_1: operand::RcProfile1::Source(operand::RcProfileSource::all()),
2605                rc_profile_n: operand::BoundedBufferOperand::try_from([
2606                    0,
2607                    0,
2608                    0,
2609                    0,
2610                    0
2611                ].as_ref()).unwrap(),
2612            },
2613            device_features: operand::DeviceFeatures {
2614                device_features_1: operand::DeviceFeatures1::all(),
2615                device_features_n: operand::BoundedBufferOperand::try_from([
2616                    0,
2617                    0,
2618                    0,
2619                    0,
2620                    0
2621                ].as_ref()).unwrap(),
2622            },
2623        },
2624        bytes: [
2625            operand::Version::V2_0 as u8,
2626            operand::AllDeviceTypes::PLAYBACK.bits(),
2627            operand::RcProfileSource::all().bits() | 0x80,
2628            0x80,
2629            0x80,
2630            0x80,
2631            0x80,
2632            0x0,
2633            operand::DeviceFeatures1::all().bits() | 0x80,
2634            0x80,
2635            0x80,
2636            0x80,
2637            0x80,
2638            0x0,
2639        ],
2640    }
2641
2642    #[test]
2643    fn test_opcode() {
2644        assert_eq!(
2645            Message::ReportFeatures {
2646                version: operand::Version::V2_0,
2647                device_types: operand::AllDeviceTypes::PLAYBACK,
2648                rc_profile: operand::RcProfile::new(operand::RcProfile1::Source(
2649                    operand::RcProfileSource::all()
2650                )),
2651                device_features: operand::DeviceFeatures::new(operand::DeviceFeatures1::all()),
2652            }
2653            .opcode(),
2654            Opcode::ReportFeatures
2655        );
2656    }
2657
2658    #[test]
2659    fn test_decoding_missing_operand_1() {
2660        assert_eq!(
2661            Message::try_from_bytes(&[
2662                Opcode::ReportFeatures as u8,
2663                operand::Version::V2_0 as u8,
2664                operand::AllDeviceTypes::PLAYBACK.bits(),
2665                operand::RcProfileSource::all().bits(),
2666            ]),
2667            Err(Error::OutOfRange {
2668                expected: Range::AtLeast(5),
2669                got: 4,
2670                quantity: "bytes",
2671            })
2672        );
2673    }
2674
2675    #[test]
2676    fn test_decoding_missing_operand_2() {
2677        assert_eq!(
2678            Message::try_from_bytes(&[
2679                Opcode::ReportFeatures as u8,
2680                operand::Version::V2_0 as u8,
2681                operand::AllDeviceTypes::PLAYBACK.bits(),
2682            ]),
2683            Err(Error::OutOfRange {
2684                expected: Range::AtLeast(5),
2685                got: 3,
2686                quantity: "bytes",
2687            })
2688        );
2689    }
2690
2691    #[test]
2692    fn test_decoding_missing_operand_3() {
2693        assert_eq!(
2694            Message::try_from_bytes(&[Opcode::ReportFeatures as u8, operand::Version::V2_0 as u8]),
2695            Err(Error::OutOfRange {
2696                expected: Range::AtLeast(5),
2697                got: 2,
2698                quantity: "bytes",
2699            })
2700        );
2701    }
2702
2703    #[test]
2704    fn test_decoding_missing_operands() {
2705        assert_eq!(
2706            Message::try_from_bytes(&[Opcode::ReportFeatures as u8]),
2707            Err(Error::OutOfRange {
2708                expected: Range::AtLeast(5),
2709                got: 1,
2710                quantity: "bytes",
2711            })
2712        );
2713    }
2714}
2715
2716#[cfg(test)]
2717mod test_request_current_latency {
2718    use super::*;
2719
2720    message_test! {
2721        ty: RequestCurrentLatency,
2722        instance: Message::RequestCurrentLatency {
2723            physical_address: PhysicalAddress(0x1234),
2724        },
2725        bytes: [0x12, 0x34],
2726        extra: [Overfull, Empty],
2727    }
2728
2729    #[test]
2730    fn test_decoding_missing_byte() {
2731        assert_eq!(
2732            Message::try_from_bytes(&[Opcode::RequestCurrentLatency as u8, 0x12]),
2733            Err(Error::OutOfRange {
2734                expected: Range::AtLeast(3),
2735                got: 2,
2736                quantity: "bytes",
2737            })
2738        );
2739    }
2740}
2741
2742#[cfg(test)]
2743mod test_report_current_latency {
2744    use super::*;
2745
2746    message_test! {
2747        ty: ReportCurrentLatency,
2748        instance: Message::ReportCurrentLatency {
2749            physical_address: PhysicalAddress(0x1234),
2750            video_latency: operand::Delay::try_from(0x56).unwrap(),
2751            flags: operand::LatencyFlags::new()
2752                .with_audio_out_compensated(operand::AudioOutputCompensated::PartialDelay)
2753                .with_low_latency_mode(true),
2754            audio_output_delay: Some(operand::Delay::try_from(0x78).unwrap()),
2755        },
2756        bytes: [0x12, 0x34, 0x56, 0x07, 0x78],
2757        extra: [Overfull, Empty],
2758    }
2759
2760    message_test! {
2761        name: _no_delay,
2762        ty: ReportCurrentLatency,
2763        instance: Message::ReportCurrentLatency {
2764            physical_address: PhysicalAddress(0x1234),
2765            video_latency: operand::Delay::try_from(0x56).unwrap(),
2766            flags: operand::LatencyFlags::new()
2767                .with_audio_out_compensated(operand::AudioOutputCompensated::NoDelay)
2768                .with_low_latency_mode(true),
2769            audio_output_delay: None,
2770        },
2771        bytes: [0x12, 0x34, 0x56, 0x06],
2772    }
2773
2774    #[test]
2775    fn test_decoding_missing_operand_1() {
2776        assert_eq!(
2777            Message::try_from_bytes(&[Opcode::ReportCurrentLatency as u8, 0x12, 0x34, 0x56]),
2778            Err(Error::OutOfRange {
2779                expected: Range::AtLeast(5),
2780                got: 4,
2781                quantity: "bytes",
2782            })
2783        );
2784    }
2785
2786    #[test]
2787    fn test_decoding_missing_operand_2() {
2788        assert_eq!(
2789            Message::try_from_bytes(&[Opcode::ReportCurrentLatency as u8, 0x12, 0x34]),
2790            Err(Error::OutOfRange {
2791                expected: Range::AtLeast(5),
2792                got: 3,
2793                quantity: "bytes",
2794            })
2795        );
2796    }
2797
2798    #[test]
2799    fn test_decoding_missing_operand_2_and_byte() {
2800        assert_eq!(
2801            Message::try_from_bytes(&[Opcode::ReportCurrentLatency as u8, 0x12]),
2802            Err(Error::OutOfRange {
2803                expected: Range::AtLeast(5),
2804                got: 2,
2805                quantity: "bytes",
2806            })
2807        );
2808    }
2809}