1use web_time::Duration;
35
36use crate::input_queue::INPUT_QUEUE_LENGTH;
37use crate::{FortressError, InvalidRequestKind};
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
72#[must_use = "SyncConfig has no effect unless passed to SessionBuilder::with_sync_config()"]
73pub struct SyncConfig {
74 pub num_sync_packets: u32,
80
81 pub sync_retry_interval: Duration,
87
88 pub sync_timeout: Option<Duration>,
93
94 pub running_retry_interval: Duration,
99
100 pub keepalive_interval: Duration,
105}
106
107impl Default for SyncConfig {
108 fn default() -> Self {
109 Self {
110 num_sync_packets: 5,
111 sync_retry_interval: Duration::from_millis(200),
112 sync_timeout: None,
113 running_retry_interval: Duration::from_millis(200),
114 keepalive_interval: Duration::from_millis(200),
115 }
116 }
117}
118
119impl std::fmt::Display for SyncConfig {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 let Self {
123 num_sync_packets,
124 sync_retry_interval,
125 sync_timeout,
126 running_retry_interval,
127 keepalive_interval,
128 } = self;
129
130 write!(
131 f,
132 "SyncConfig {{ num_sync_packets: {}, sync_retry: {:?}, timeout: {}, running_retry: {:?}, keepalive: {:?} }}",
133 num_sync_packets,
134 sync_retry_interval,
135 sync_timeout.map_or_else(|| "None".to_string(), |d| format!("{:?}", d)),
136 running_retry_interval,
137 keepalive_interval,
138 )
139 }
140}
141
142impl SyncConfig {
143 pub fn new() -> Self {
145 Self::default()
146 }
147
148 pub fn high_latency() -> Self {
152 Self {
153 num_sync_packets: 5,
154 sync_retry_interval: Duration::from_millis(400),
155 sync_timeout: Some(Duration::from_secs(10)),
156 running_retry_interval: Duration::from_millis(400),
157 keepalive_interval: Duration::from_millis(400),
158 }
159 }
160
161 pub fn lossy() -> Self {
176 Self {
177 num_sync_packets: 8,
178 sync_retry_interval: Duration::from_millis(200),
179 sync_timeout: Some(Duration::from_secs(10)),
180 running_retry_interval: Duration::from_millis(200),
181 keepalive_interval: Duration::from_millis(200),
182 }
183 }
184
185 pub fn lan() -> Self {
189 Self {
190 num_sync_packets: 3,
191 sync_retry_interval: Duration::from_millis(100),
192 sync_timeout: Some(Duration::from_secs(5)),
193 running_retry_interval: Duration::from_millis(100),
194 keepalive_interval: Duration::from_millis(100),
195 }
196 }
197
198 pub fn mobile() -> Self {
210 Self {
211 num_sync_packets: 10,
213 sync_retry_interval: Duration::from_millis(350),
215 sync_timeout: Some(Duration::from_secs(15)),
217 running_retry_interval: Duration::from_millis(350),
219 keepalive_interval: Duration::from_millis(300),
221 }
222 }
223
224 pub fn competitive() -> Self {
234 Self {
235 num_sync_packets: 4,
237 sync_retry_interval: Duration::from_millis(100),
239 sync_timeout: Some(Duration::from_secs(3)),
241 running_retry_interval: Duration::from_millis(100),
243 keepalive_interval: Duration::from_millis(100),
245 }
246 }
247
248 pub fn extreme() -> Self {
273 Self {
274 num_sync_packets: 20,
278 sync_retry_interval: Duration::from_millis(250),
280 sync_timeout: Some(Duration::from_secs(30)),
282 running_retry_interval: Duration::from_millis(250),
284 keepalive_interval: Duration::from_millis(200),
286 }
287 }
288
289 pub fn stress_test() -> Self {
323 Self {
324 num_sync_packets: 40,
327 sync_retry_interval: Duration::from_millis(150),
330 sync_timeout: Some(Duration::from_secs(60)),
333 running_retry_interval: Duration::from_millis(150),
335 keepalive_interval: Duration::from_millis(150),
337 }
338 }
339}
340
341#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
374#[must_use = "ProtocolConfig has no effect unless passed to SessionBuilder::with_protocol_config()"]
375pub struct ProtocolConfig {
376 pub quality_report_interval: Duration,
383
384 pub shutdown_delay: Duration,
391
392 pub max_checksum_history: usize,
399
400 pub pending_output_limit: usize,
408
409 pub sync_retry_warning_threshold: u32,
417
418 pub sync_duration_warning_ms: u128,
425
426 pub input_history_multiplier: usize,
437
438 pub protocol_rng_seed: Option<u64>,
471}
472
473impl Default for ProtocolConfig {
474 fn default() -> Self {
475 Self {
476 quality_report_interval: Duration::from_millis(200),
477 shutdown_delay: Duration::from_millis(5000),
478 max_checksum_history: 32,
479 pending_output_limit: 128,
480 sync_retry_warning_threshold: 10,
481 sync_duration_warning_ms: 3000,
482 input_history_multiplier: 2,
483 protocol_rng_seed: None,
484 }
485 }
486}
487
488impl std::fmt::Display for ProtocolConfig {
489 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
490 let Self {
492 quality_report_interval,
493 shutdown_delay,
494 max_checksum_history,
495 pending_output_limit,
496 sync_retry_warning_threshold,
497 sync_duration_warning_ms,
498 input_history_multiplier,
499 protocol_rng_seed,
500 } = self;
501
502 write!(
503 f,
504 "ProtocolConfig {{ quality_report: {:?}, shutdown: {:?}, checksum_history: {}, pending_limit: {}, retry_warn: {}, duration_warn_ms: {}, history_mult: {}, seed: {} }}",
505 quality_report_interval,
506 shutdown_delay,
507 max_checksum_history,
508 pending_output_limit,
509 sync_retry_warning_threshold,
510 sync_duration_warning_ms,
511 input_history_multiplier,
512 protocol_rng_seed.map_or_else(|| "None".to_string(), |s| s.to_string()),
513 )
514 }
515}
516
517impl ProtocolConfig {
518 pub fn new() -> Self {
520 Self::default()
521 }
522
523 pub fn competitive() -> Self {
528 Self {
529 quality_report_interval: Duration::from_millis(100),
530 shutdown_delay: Duration::from_millis(3000),
531 max_checksum_history: 32,
532 pending_output_limit: 128,
533 sync_retry_warning_threshold: 10,
534 sync_duration_warning_ms: 2000,
535 input_history_multiplier: 2,
536 protocol_rng_seed: None,
537 }
538 }
539
540 pub fn high_latency() -> Self {
545 Self {
546 quality_report_interval: Duration::from_millis(400),
547 shutdown_delay: Duration::from_millis(10000),
548 max_checksum_history: 64,
549 pending_output_limit: 256,
550 sync_retry_warning_threshold: 20,
551 sync_duration_warning_ms: 10000,
552 input_history_multiplier: 3,
553 protocol_rng_seed: None,
554 }
555 }
556
557 pub fn debug() -> Self {
562 Self {
563 quality_report_interval: Duration::from_millis(500),
564 shutdown_delay: Duration::from_millis(30000),
565 max_checksum_history: 128,
566 pending_output_limit: 64,
567 sync_retry_warning_threshold: 5,
568 sync_duration_warning_ms: 1000,
569 input_history_multiplier: 4,
570 protocol_rng_seed: None,
571 }
572 }
573
574 pub fn mobile() -> Self {
585 Self {
586 quality_report_interval: Duration::from_millis(350),
588 shutdown_delay: Duration::from_millis(15000),
590 max_checksum_history: 64,
592 pending_output_limit: 256,
594 sync_retry_warning_threshold: 25,
596 sync_duration_warning_ms: 12000,
598 input_history_multiplier: 3,
600 protocol_rng_seed: None,
601 }
602 }
603
604 pub fn deterministic(seed: u64) -> Self {
627 Self {
628 protocol_rng_seed: Some(seed),
629 ..Self::default()
630 }
631 }
632
633 pub fn validate(&self) -> Result<(), FortressError> {
639 if self.quality_report_interval < Duration::from_millis(1)
641 || self.quality_report_interval > Duration::from_millis(10000)
642 {
643 return Err(InvalidRequestKind::DurationConfigOutOfRange {
644 field: "quality_report_interval",
645 min_ms: 1,
646 max_ms: 10000,
647 actual_ms: self.quality_report_interval.as_millis() as u64,
648 }
649 .into());
650 }
651
652 if self.shutdown_delay < Duration::from_millis(1)
654 || self.shutdown_delay > Duration::from_millis(300000)
655 {
656 return Err(InvalidRequestKind::DurationConfigOutOfRange {
657 field: "shutdown_delay",
658 min_ms: 1,
659 max_ms: 300000,
660 actual_ms: self.shutdown_delay.as_millis() as u64,
661 }
662 .into());
663 }
664
665 if self.max_checksum_history < 1 || self.max_checksum_history > 1024 {
667 return Err(InvalidRequestKind::ConfigValueOutOfRange {
668 field: "max_checksum_history",
669 min: 1,
670 max: 1024,
671 actual: self.max_checksum_history as u64,
672 }
673 .into());
674 }
675
676 if self.pending_output_limit < 1 || self.pending_output_limit > 4096 {
678 return Err(InvalidRequestKind::ConfigValueOutOfRange {
679 field: "pending_output_limit",
680 min: 1,
681 max: 4096,
682 actual: self.pending_output_limit as u64,
683 }
684 .into());
685 }
686
687 if self.sync_retry_warning_threshold < 1 || self.sync_retry_warning_threshold > 1000 {
689 return Err(InvalidRequestKind::ConfigValueOutOfRange {
690 field: "sync_retry_warning_threshold",
691 min: 1,
692 max: 1000,
693 actual: self.sync_retry_warning_threshold as u64,
694 }
695 .into());
696 }
697
698 if self.sync_duration_warning_ms < 1 || self.sync_duration_warning_ms > 300000 {
700 return Err(InvalidRequestKind::ConfigValueOutOfRange {
701 field: "sync_duration_warning_ms",
702 min: 1,
703 max: 300000,
704 actual: self.sync_duration_warning_ms as u64,
705 }
706 .into());
707 }
708
709 if self.input_history_multiplier < 1 || self.input_history_multiplier > 16 {
711 return Err(InvalidRequestKind::ConfigValueOutOfRange {
712 field: "input_history_multiplier",
713 min: 1,
714 max: 16,
715 actual: self.input_history_multiplier as u64,
716 }
717 .into());
718 }
719
720 Ok(())
721 }
722}
723
724#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
750#[must_use = "SpectatorConfig has no effect unless passed to SessionBuilder::with_spectator_config()"]
751pub struct SpectatorConfig {
752 pub buffer_size: usize,
761
762 pub catchup_speed: usize,
770
771 pub max_frames_behind: usize,
778}
779
780impl Default for SpectatorConfig {
781 fn default() -> Self {
782 Self {
783 buffer_size: 60,
784 catchup_speed: 1,
785 max_frames_behind: 10,
786 }
787 }
788}
789
790impl std::fmt::Display for SpectatorConfig {
791 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
792 let Self {
794 buffer_size,
795 catchup_speed,
796 max_frames_behind,
797 } = self;
798
799 write!(
800 f,
801 "SpectatorConfig {{ buffer: {}, catchup_speed: {}, max_behind: {} }}",
802 buffer_size, catchup_speed, max_frames_behind,
803 )
804 }
805}
806
807impl SpectatorConfig {
808 pub fn new() -> Self {
810 Self::default()
811 }
812
813 pub fn fast_paced() -> Self {
818 Self {
819 buffer_size: 90,
820 catchup_speed: 2,
821 max_frames_behind: 15,
822 }
823 }
824
825 pub fn slow_connection() -> Self {
829 Self {
830 buffer_size: 120,
831 catchup_speed: 1,
832 max_frames_behind: 20,
833 }
834 }
835
836 pub fn local() -> Self {
840 Self {
841 buffer_size: 30,
842 catchup_speed: 2,
843 max_frames_behind: 5,
844 }
845 }
846
847 pub fn broadcast() -> Self {
858 Self {
859 buffer_size: 180,
861 catchup_speed: 1,
863 max_frames_behind: 30,
865 }
866 }
867
868 pub fn mobile() -> Self {
873 Self {
874 buffer_size: 120,
876 catchup_speed: 1,
878 max_frames_behind: 25,
880 }
881 }
882}
883
884#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
925#[must_use = "InputQueueConfig has no effect unless passed to SessionBuilder::with_input_queue_config()"]
926pub struct InputQueueConfig {
927 pub queue_length: usize,
949}
950
951impl Default for InputQueueConfig {
952 fn default() -> Self {
953 Self {
954 queue_length: INPUT_QUEUE_LENGTH,
955 }
956 }
957}
958
959impl std::fmt::Display for InputQueueConfig {
960 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
961 let Self { queue_length } = self;
963 write!(f, "InputQueueConfig {{ queue_length: {} }}", queue_length)
964 }
965}
966
967impl InputQueueConfig {
968 pub fn new() -> Self {
970 Self::default()
971 }
972
973 pub fn high_latency() -> Self {
978 Self { queue_length: 256 }
979 }
980
981 pub fn minimal() -> Self {
986 Self { queue_length: 32 }
987 }
988
989 pub fn standard() -> Self {
993 Self::default()
994 }
995
996 #[must_use]
1001 pub fn max_frame_delay(&self) -> usize {
1002 self.queue_length.saturating_sub(1)
1003 }
1004
1005 pub fn validate_frame_delay(&self, frame_delay: usize) -> Result<(), FortressError> {
1011 if frame_delay >= self.queue_length {
1012 return Err(InvalidRequestKind::FrameDelayTooLarge {
1013 delay: frame_delay,
1014 max_delay: self.max_frame_delay(),
1015 }
1016 .into());
1017 }
1018 Ok(())
1019 }
1020
1021 pub fn validate(&self) -> Result<(), FortressError> {
1027 if self.queue_length < 2 {
1028 return Err(InvalidRequestKind::QueueLengthTooSmall {
1029 length: self.queue_length,
1030 }
1031 .into());
1032 }
1033 Ok(())
1034 }
1035}
1036
1037#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
1075pub enum SaveMode {
1076 #[default]
1087 EveryFrame,
1088
1089 Sparse,
1100}
1101
1102impl std::fmt::Display for SaveMode {
1103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1104 match self {
1105 Self::EveryFrame => write!(f, "EveryFrame"),
1106 Self::Sparse => write!(f, "Sparse"),
1107 }
1108 }
1109}
1110
1111#[cfg(test)]
1116#[allow(
1117 clippy::panic,
1118 clippy::unwrap_used,
1119 clippy::expect_used,
1120 clippy::indexing_slicing
1121)]
1122mod tests {
1123 use super::*;
1124
1125 #[test]
1130 fn test_save_mode_default_is_every_frame() {
1131 let mode = SaveMode::default();
1132 assert_eq!(mode, SaveMode::EveryFrame);
1133 }
1134
1135 #[test]
1136 fn test_save_mode_equality() {
1137 assert_eq!(SaveMode::EveryFrame, SaveMode::EveryFrame);
1138 assert_eq!(SaveMode::Sparse, SaveMode::Sparse);
1139 assert_ne!(SaveMode::EveryFrame, SaveMode::Sparse);
1140 }
1141
1142 #[test]
1143 fn test_save_mode_debug_format() {
1144 let every_frame = SaveMode::EveryFrame;
1145 let sparse = SaveMode::Sparse;
1146 assert_eq!(format!("{:?}", every_frame), "EveryFrame");
1147 assert_eq!(format!("{:?}", sparse), "Sparse");
1148 }
1149
1150 #[test]
1151 fn test_save_mode_display() {
1152 assert_eq!(SaveMode::EveryFrame.to_string(), "EveryFrame");
1153 assert_eq!(SaveMode::Sparse.to_string(), "Sparse");
1154 }
1155
1156 #[test]
1157 fn test_save_mode_clone() {
1158 let mode = SaveMode::Sparse;
1159 let cloned = Clone::clone(&mode);
1161 assert_eq!(mode, cloned);
1162 }
1163
1164 #[test]
1165 fn test_save_mode_copy() {
1166 let mode = SaveMode::EveryFrame;
1167 let copied: SaveMode = mode; assert_eq!(mode, copied);
1169 }
1170
1171 #[test]
1176 fn test_input_queue_config_presets() {
1177 let high_latency = InputQueueConfig::high_latency();
1178 assert_eq!(high_latency.queue_length, 256);
1179
1180 let minimal = InputQueueConfig::minimal();
1181 assert_eq!(minimal.queue_length, 32);
1182
1183 let standard = InputQueueConfig::standard();
1184 assert_eq!(standard, InputQueueConfig::default());
1185 }
1186
1187 #[test]
1193 fn test_standard_preset_uses_input_queue_length_constant() {
1194 let standard = InputQueueConfig::standard();
1195 assert_eq!(
1196 standard.queue_length, INPUT_QUEUE_LENGTH,
1197 "standard() should return INPUT_QUEUE_LENGTH ({}), but got {}. \
1198 This may indicate a hardcoded value that doesn't account for \
1199 different build configurations (e.g., Kani vs production).",
1200 INPUT_QUEUE_LENGTH, standard.queue_length
1201 );
1202 }
1203
1204 #[test]
1208 fn test_all_presets_are_valid_configurations() {
1209 let presets: &[(&str, InputQueueConfig)] = &[
1210 ("standard", InputQueueConfig::standard()),
1211 ("high_latency", InputQueueConfig::high_latency()),
1212 ("minimal", InputQueueConfig::minimal()),
1213 ];
1214
1215 for (name, config) in presets {
1216 assert!(
1217 config.validate().is_ok(),
1218 "Preset '{}' with queue_length={} should be valid, but validation failed: {:?}",
1219 name,
1220 config.queue_length,
1221 config.validate()
1222 );
1223 }
1224 }
1225
1226 #[test]
1228 fn test_all_presets_max_frame_delay_is_valid() {
1229 let presets: &[(&str, InputQueueConfig)] = &[
1230 ("standard", InputQueueConfig::standard()),
1231 ("high_latency", InputQueueConfig::high_latency()),
1232 ("minimal", InputQueueConfig::minimal()),
1233 ];
1234
1235 for (name, config) in presets {
1236 let max_delay = config.max_frame_delay();
1237 assert!(
1238 config.validate_frame_delay(max_delay).is_ok(),
1239 "Preset '{}': max_frame_delay() returned {}, but this is not a valid frame delay \
1240 for queue_length={}",
1241 name,
1242 max_delay,
1243 config.queue_length
1244 );
1245 }
1246 }
1247
1248 #[test]
1254 fn test_hardcoded_preset_values() {
1255 assert_eq!(
1257 InputQueueConfig::high_latency().queue_length,
1258 256,
1259 "high_latency() is documented to return queue_length=256"
1260 );
1261
1262 assert_eq!(
1264 InputQueueConfig::minimal().queue_length,
1265 32,
1266 "minimal() is documented to return queue_length=32"
1267 );
1268 }
1269
1270 #[test]
1271 fn test_input_queue_config_max_frame_delay() {
1272 let config = InputQueueConfig { queue_length: 64 };
1273 assert_eq!(config.max_frame_delay(), 63);
1274
1275 let config = InputQueueConfig { queue_length: 128 };
1276 assert_eq!(config.max_frame_delay(), 127);
1277 }
1278
1279 #[test]
1280 fn test_input_queue_config_validate() {
1281 assert!(InputQueueConfig { queue_length: 2 }.validate().is_ok());
1283 assert!(InputQueueConfig { queue_length: 128 }.validate().is_ok());
1284
1285 assert!(InputQueueConfig { queue_length: 0 }.validate().is_err());
1287 assert!(InputQueueConfig { queue_length: 1 }.validate().is_err());
1288 }
1289
1290 #[test]
1291 fn test_input_queue_config_validate_frame_delay() {
1292 let config = InputQueueConfig { queue_length: 32 };
1293
1294 assert!(config.validate_frame_delay(0).is_ok());
1296 assert!(config.validate_frame_delay(31).is_ok());
1297
1298 assert!(config.validate_frame_delay(32).is_err());
1300 assert!(config.validate_frame_delay(100).is_err());
1301 }
1302
1303 #[test]
1304 fn test_input_queue_config_display() {
1305 let config = InputQueueConfig { queue_length: 128 };
1306 assert_eq!(config.to_string(), "InputQueueConfig { queue_length: 128 }");
1307
1308 let config = InputQueueConfig { queue_length: 256 };
1309 assert_eq!(config.to_string(), "InputQueueConfig { queue_length: 256 }");
1310 }
1311
1312 #[test]
1317 fn sync_config_default_values() {
1318 let config = SyncConfig::default();
1319 assert_eq!(config.num_sync_packets, 5);
1320 assert_eq!(config.sync_retry_interval, Duration::from_millis(200));
1321 assert!(config.sync_timeout.is_none());
1322 assert_eq!(config.running_retry_interval, Duration::from_millis(200));
1323 assert_eq!(config.keepalive_interval, Duration::from_millis(200));
1324 }
1325
1326 #[test]
1327 fn sync_config_new_equals_default() {
1328 let new_config = SyncConfig::new();
1329 let default_config = SyncConfig::default();
1330 assert_eq!(new_config, default_config);
1331 }
1332
1333 #[test]
1334 fn sync_config_high_latency_preset() {
1335 let config = SyncConfig::high_latency();
1336 assert_eq!(config.num_sync_packets, 5);
1337 assert_eq!(config.sync_retry_interval, Duration::from_millis(400));
1338 assert_eq!(config.sync_timeout, Some(Duration::from_secs(10)));
1339 assert_eq!(config.running_retry_interval, Duration::from_millis(400));
1340 assert_eq!(config.keepalive_interval, Duration::from_millis(400));
1341 }
1342
1343 #[test]
1344 fn sync_config_lossy_preset() {
1345 let config = SyncConfig::lossy();
1346 assert_eq!(config.num_sync_packets, 8);
1347 assert_eq!(config.sync_retry_interval, Duration::from_millis(200));
1348 assert_eq!(config.sync_timeout, Some(Duration::from_secs(10)));
1349 assert_eq!(config.running_retry_interval, Duration::from_millis(200));
1350 assert_eq!(config.keepalive_interval, Duration::from_millis(200));
1351 }
1352
1353 #[test]
1354 fn sync_config_lan_preset() {
1355 let config = SyncConfig::lan();
1356 assert_eq!(config.num_sync_packets, 3);
1357 assert_eq!(config.sync_retry_interval, Duration::from_millis(100));
1358 assert_eq!(config.sync_timeout, Some(Duration::from_secs(5)));
1359 assert_eq!(config.running_retry_interval, Duration::from_millis(100));
1360 assert_eq!(config.keepalive_interval, Duration::from_millis(100));
1361 }
1362
1363 #[test]
1364 fn sync_config_mobile_preset() {
1365 let config = SyncConfig::mobile();
1366 assert_eq!(config.num_sync_packets, 10);
1367 assert_eq!(config.sync_retry_interval, Duration::from_millis(350));
1368 assert_eq!(config.sync_timeout, Some(Duration::from_secs(15)));
1369 assert_eq!(config.running_retry_interval, Duration::from_millis(350));
1370 assert_eq!(config.keepalive_interval, Duration::from_millis(300));
1371 }
1372
1373 #[test]
1374 fn sync_config_competitive_preset() {
1375 let config = SyncConfig::competitive();
1376 assert_eq!(config.num_sync_packets, 4);
1377 assert_eq!(config.sync_retry_interval, Duration::from_millis(100));
1378 assert_eq!(config.sync_timeout, Some(Duration::from_secs(3)));
1379 assert_eq!(config.running_retry_interval, Duration::from_millis(100));
1380 assert_eq!(config.keepalive_interval, Duration::from_millis(100));
1381 }
1382
1383 #[test]
1384 fn sync_config_extreme_preset() {
1385 let config = SyncConfig::extreme();
1386 assert_eq!(config.num_sync_packets, 20);
1387 assert_eq!(config.sync_retry_interval, Duration::from_millis(250));
1388 assert_eq!(config.sync_timeout, Some(Duration::from_secs(30)));
1389 assert_eq!(config.running_retry_interval, Duration::from_millis(250));
1390 assert_eq!(config.keepalive_interval, Duration::from_millis(200));
1391 }
1392
1393 #[test]
1394 fn sync_config_stress_test_preset() {
1395 let config = SyncConfig::stress_test();
1396 assert_eq!(config.num_sync_packets, 40);
1397 assert_eq!(config.sync_retry_interval, Duration::from_millis(150));
1398 assert_eq!(config.sync_timeout, Some(Duration::from_secs(60)));
1399 assert_eq!(config.running_retry_interval, Duration::from_millis(150));
1400 assert_eq!(config.keepalive_interval, Duration::from_millis(150));
1401 }
1402
1403 #[test]
1404 fn sync_config_equality() {
1405 let config1 = SyncConfig::default();
1406 let config2 = SyncConfig::default();
1407 let config3 = SyncConfig::lan();
1408 assert_eq!(config1, config2);
1409 assert_ne!(config1, config3);
1410 }
1411
1412 #[test]
1413 #[allow(clippy::clone_on_copy)] fn sync_config_clone() {
1415 let config = SyncConfig::high_latency();
1416 let cloned = config.clone();
1417 assert_eq!(config, cloned);
1418 }
1419
1420 #[test]
1421 fn sync_config_copy() {
1422 let config = SyncConfig::lossy();
1423 let copied: SyncConfig = config; assert_eq!(config, copied);
1425 }
1426
1427 #[test]
1428 fn sync_config_debug_format() {
1429 let config = SyncConfig::default();
1430 let debug_str = format!("{:?}", config);
1431 assert!(debug_str.contains("SyncConfig"));
1432 assert!(debug_str.contains("num_sync_packets"));
1433 assert!(debug_str.contains("sync_retry_interval"));
1434 }
1435
1436 #[test]
1437 fn sync_config_display() {
1438 let config = SyncConfig::default();
1440 let display_str = config.to_string();
1441 assert!(display_str.contains("SyncConfig"));
1442 assert!(display_str.contains("num_sync_packets: 5"));
1443 assert!(display_str.contains("timeout: None"));
1444
1445 let config = SyncConfig::lan();
1447 let display_str = config.to_string();
1448 assert!(display_str.contains("SyncConfig"));
1449 assert!(display_str.contains("num_sync_packets: 3"));
1450 assert!(display_str.contains("5s")); }
1452
1453 #[test]
1454 fn sync_config_presets_differ() {
1455 let presets = [
1457 SyncConfig::default(),
1458 SyncConfig::high_latency(),
1459 SyncConfig::lossy(),
1460 SyncConfig::lan(),
1461 SyncConfig::mobile(),
1462 SyncConfig::competitive(),
1463 ];
1464
1465 for (i, preset_a) in presets.iter().enumerate() {
1467 for (j, preset_b) in presets.iter().enumerate() {
1468 if i != j {
1469 assert_ne!(
1470 preset_a, preset_b,
1471 "Presets at index {} and {} should differ",
1472 i, j
1473 );
1474 }
1475 }
1476 }
1477 }
1478
1479 #[test]
1484 fn protocol_config_default_values() {
1485 let config = ProtocolConfig::default();
1486 assert_eq!(config.quality_report_interval, Duration::from_millis(200));
1487 assert_eq!(config.shutdown_delay, Duration::from_millis(5000));
1488 assert_eq!(config.max_checksum_history, 32);
1489 assert_eq!(config.pending_output_limit, 128);
1490 assert_eq!(config.sync_retry_warning_threshold, 10);
1491 assert_eq!(config.sync_duration_warning_ms, 3000);
1492 }
1493
1494 #[test]
1495 fn protocol_config_new_equals_default() {
1496 let new_config = ProtocolConfig::new();
1497 let default_config = ProtocolConfig::default();
1498 assert_eq!(new_config, default_config);
1499 }
1500
1501 #[test]
1502 fn protocol_config_competitive_preset() {
1503 let config = ProtocolConfig::competitive();
1504 assert_eq!(config.quality_report_interval, Duration::from_millis(100));
1505 assert_eq!(config.shutdown_delay, Duration::from_millis(3000));
1506 assert_eq!(config.max_checksum_history, 32);
1507 assert_eq!(config.pending_output_limit, 128);
1508 assert_eq!(config.sync_retry_warning_threshold, 10);
1509 assert_eq!(config.sync_duration_warning_ms, 2000);
1510 }
1511
1512 #[test]
1513 fn protocol_config_high_latency_preset() {
1514 let config = ProtocolConfig::high_latency();
1515 assert_eq!(config.quality_report_interval, Duration::from_millis(400));
1516 assert_eq!(config.shutdown_delay, Duration::from_millis(10000));
1517 assert_eq!(config.max_checksum_history, 64);
1518 assert_eq!(config.pending_output_limit, 256);
1519 assert_eq!(config.sync_retry_warning_threshold, 20);
1520 assert_eq!(config.sync_duration_warning_ms, 10000);
1521 }
1522
1523 #[test]
1524 fn protocol_config_debug_preset() {
1525 let config = ProtocolConfig::debug();
1526 assert_eq!(config.quality_report_interval, Duration::from_millis(500));
1527 assert_eq!(config.shutdown_delay, Duration::from_millis(30000));
1528 assert_eq!(config.max_checksum_history, 128);
1529 assert_eq!(config.pending_output_limit, 64);
1530 assert_eq!(config.sync_retry_warning_threshold, 5);
1531 assert_eq!(config.sync_duration_warning_ms, 1000);
1532 }
1533
1534 #[test]
1535 fn protocol_config_mobile_preset() {
1536 let config = ProtocolConfig::mobile();
1537 assert_eq!(config.quality_report_interval, Duration::from_millis(350));
1538 assert_eq!(config.shutdown_delay, Duration::from_millis(15000));
1539 assert_eq!(config.max_checksum_history, 64);
1540 assert_eq!(config.pending_output_limit, 256);
1541 assert_eq!(config.sync_retry_warning_threshold, 25);
1542 assert_eq!(config.sync_duration_warning_ms, 12000);
1543 }
1544
1545 #[test]
1546 fn protocol_config_equality() {
1547 let config1 = ProtocolConfig::default();
1548 let config2 = ProtocolConfig::default();
1549 let config3 = ProtocolConfig::competitive();
1550 assert_eq!(config1, config2);
1551 assert_ne!(config1, config3);
1552 }
1553
1554 #[test]
1555 #[allow(clippy::clone_on_copy)] fn protocol_config_clone() {
1557 let config = ProtocolConfig::high_latency();
1558 let cloned = config.clone();
1559 assert_eq!(config, cloned);
1560 }
1561
1562 #[test]
1563 fn protocol_config_copy() {
1564 let config = ProtocolConfig::mobile();
1565 let copied: ProtocolConfig = config; assert_eq!(config, copied);
1567 }
1568
1569 #[test]
1570 fn protocol_config_debug_format() {
1571 let config = ProtocolConfig::default();
1572 let debug_str = format!("{:?}", config);
1573 assert!(debug_str.contains("ProtocolConfig"));
1574 assert!(debug_str.contains("quality_report_interval"));
1575 assert!(debug_str.contains("shutdown_delay"));
1576 }
1577
1578 #[test]
1579 fn protocol_config_display() {
1580 let config = ProtocolConfig::default();
1582 let display_str = config.to_string();
1583 assert!(display_str.contains("ProtocolConfig"));
1584 assert!(display_str.contains("checksum_history: 32"));
1585 assert!(display_str.contains("pending_limit: 128"));
1586 assert!(display_str.contains("seed: None"));
1587
1588 let config = ProtocolConfig::deterministic(42);
1590 let display_str = config.to_string();
1591 assert!(display_str.contains("ProtocolConfig"));
1592 assert!(display_str.contains("seed: 42"));
1593 }
1594
1595 #[test]
1596 fn protocol_config_presets_differ() {
1597 let presets = [
1599 ProtocolConfig::default(),
1600 ProtocolConfig::competitive(),
1601 ProtocolConfig::high_latency(),
1602 ProtocolConfig::debug(),
1603 ProtocolConfig::mobile(),
1604 ];
1605
1606 for (i, preset_a) in presets.iter().enumerate() {
1607 for (j, preset_b) in presets.iter().enumerate() {
1608 if i != j {
1609 assert_ne!(
1610 preset_a, preset_b,
1611 "ProtocolConfig presets at index {} and {} should differ",
1612 i, j
1613 );
1614 }
1615 }
1616 }
1617 }
1618
1619 #[test]
1624 fn test_protocol_config_validate_default_is_valid() {
1625 let config = ProtocolConfig::default();
1626 config.validate().unwrap();
1627 }
1628
1629 #[test]
1630 fn test_protocol_config_validate_all_presets_are_valid() {
1631 let presets: &[(&str, ProtocolConfig)] = &[
1632 ("default", ProtocolConfig::default()),
1633 ("competitive", ProtocolConfig::competitive()),
1634 ("high_latency", ProtocolConfig::high_latency()),
1635 ("debug", ProtocolConfig::debug()),
1636 ("mobile", ProtocolConfig::mobile()),
1637 ];
1638
1639 for (name, config) in presets {
1640 assert!(
1641 config.validate().is_ok(),
1642 "Preset '{}' should be valid, but validation failed: {:?}",
1643 name,
1644 config.validate()
1645 );
1646 }
1647 }
1648
1649 #[test]
1650 fn test_protocol_config_validate_quality_report_interval_valid() {
1651 let config = ProtocolConfig {
1653 quality_report_interval: Duration::from_millis(1),
1654 ..ProtocolConfig::default()
1655 };
1656 config.validate().unwrap();
1657
1658 let config = ProtocolConfig {
1660 quality_report_interval: Duration::from_millis(10000),
1661 ..ProtocolConfig::default()
1662 };
1663 config.validate().unwrap();
1664
1665 let config = ProtocolConfig {
1667 quality_report_interval: Duration::from_millis(500),
1668 ..ProtocolConfig::default()
1669 };
1670 config.validate().unwrap();
1671 }
1672
1673 #[test]
1674 fn test_protocol_config_validate_quality_report_interval_too_low() {
1675 let config = ProtocolConfig {
1677 quality_report_interval: Duration::from_millis(0),
1678 ..ProtocolConfig::default()
1679 };
1680 let result = config.validate();
1681 assert!(result.is_err());
1682 let err = result.unwrap_err();
1683 assert!(matches!(
1684 err,
1685 FortressError::InvalidRequestStructured {
1686 kind: InvalidRequestKind::DurationConfigOutOfRange {
1687 field: "quality_report_interval",
1688 min_ms: 1,
1689 max_ms: 10000,
1690 ..
1691 }
1692 }
1693 ));
1694 }
1695
1696 #[test]
1697 fn test_protocol_config_validate_quality_report_interval_too_high() {
1698 let config = ProtocolConfig {
1700 quality_report_interval: Duration::from_millis(10001),
1701 ..ProtocolConfig::default()
1702 };
1703 let result = config.validate();
1704 assert!(result.is_err());
1705 let err = result.unwrap_err();
1706 assert!(matches!(
1707 err,
1708 FortressError::InvalidRequestStructured {
1709 kind: InvalidRequestKind::DurationConfigOutOfRange {
1710 field: "quality_report_interval",
1711 min_ms: 1,
1712 max_ms: 10000,
1713 ..
1714 }
1715 }
1716 ));
1717 }
1718
1719 #[test]
1720 fn test_protocol_config_validate_shutdown_delay_valid() {
1721 let config = ProtocolConfig {
1723 shutdown_delay: Duration::from_millis(1),
1724 ..ProtocolConfig::default()
1725 };
1726 config.validate().unwrap();
1727
1728 let config = ProtocolConfig {
1730 shutdown_delay: Duration::from_millis(300000),
1731 ..ProtocolConfig::default()
1732 };
1733 config.validate().unwrap();
1734
1735 let config = ProtocolConfig {
1737 shutdown_delay: Duration::from_millis(10000),
1738 ..ProtocolConfig::default()
1739 };
1740 config.validate().unwrap();
1741 }
1742
1743 #[test]
1744 fn test_protocol_config_validate_shutdown_delay_too_low() {
1745 let config = ProtocolConfig {
1747 shutdown_delay: Duration::from_millis(0),
1748 ..ProtocolConfig::default()
1749 };
1750 let result = config.validate();
1751 assert!(result.is_err());
1752 let err = result.unwrap_err();
1753 assert!(matches!(
1754 err,
1755 FortressError::InvalidRequestStructured {
1756 kind: InvalidRequestKind::DurationConfigOutOfRange {
1757 field: "shutdown_delay",
1758 min_ms: 1,
1759 max_ms: 300000,
1760 ..
1761 }
1762 }
1763 ));
1764 }
1765
1766 #[test]
1767 fn test_protocol_config_validate_shutdown_delay_too_high() {
1768 let config = ProtocolConfig {
1770 shutdown_delay: Duration::from_millis(300001),
1771 ..ProtocolConfig::default()
1772 };
1773 let result = config.validate();
1774 assert!(result.is_err());
1775 let err = result.unwrap_err();
1776 assert!(matches!(
1777 err,
1778 FortressError::InvalidRequestStructured {
1779 kind: InvalidRequestKind::DurationConfigOutOfRange {
1780 field: "shutdown_delay",
1781 min_ms: 1,
1782 max_ms: 300000,
1783 ..
1784 }
1785 }
1786 ));
1787 }
1788
1789 #[test]
1790 fn test_protocol_config_validate_max_checksum_history_valid() {
1791 let config = ProtocolConfig {
1793 max_checksum_history: 1,
1794 ..ProtocolConfig::default()
1795 };
1796 config.validate().unwrap();
1797
1798 let config = ProtocolConfig {
1800 max_checksum_history: 1024,
1801 ..ProtocolConfig::default()
1802 };
1803 config.validate().unwrap();
1804
1805 let config = ProtocolConfig {
1807 max_checksum_history: 64,
1808 ..ProtocolConfig::default()
1809 };
1810 config.validate().unwrap();
1811 }
1812
1813 #[test]
1814 fn test_protocol_config_validate_max_checksum_history_too_low() {
1815 let config = ProtocolConfig {
1817 max_checksum_history: 0,
1818 ..ProtocolConfig::default()
1819 };
1820 let result = config.validate();
1821 assert!(result.is_err());
1822 let err = result.unwrap_err();
1823 assert!(matches!(
1824 err,
1825 FortressError::InvalidRequestStructured {
1826 kind: InvalidRequestKind::ConfigValueOutOfRange {
1827 field: "max_checksum_history",
1828 min: 1,
1829 max: 1024,
1830 ..
1831 }
1832 }
1833 ));
1834 }
1835
1836 #[test]
1837 fn test_protocol_config_validate_max_checksum_history_too_high() {
1838 let config = ProtocolConfig {
1840 max_checksum_history: 1025,
1841 ..ProtocolConfig::default()
1842 };
1843 let result = config.validate();
1844 assert!(result.is_err());
1845 let err = result.unwrap_err();
1846 assert!(matches!(
1847 err,
1848 FortressError::InvalidRequestStructured {
1849 kind: InvalidRequestKind::ConfigValueOutOfRange {
1850 field: "max_checksum_history",
1851 min: 1,
1852 max: 1024,
1853 ..
1854 }
1855 }
1856 ));
1857 }
1858
1859 #[test]
1860 fn test_protocol_config_validate_pending_output_limit_valid() {
1861 let config = ProtocolConfig {
1863 pending_output_limit: 1,
1864 ..ProtocolConfig::default()
1865 };
1866 config.validate().unwrap();
1867
1868 let config = ProtocolConfig {
1870 pending_output_limit: 4096,
1871 ..ProtocolConfig::default()
1872 };
1873 config.validate().unwrap();
1874
1875 let config = ProtocolConfig {
1877 pending_output_limit: 256,
1878 ..ProtocolConfig::default()
1879 };
1880 config.validate().unwrap();
1881 }
1882
1883 #[test]
1884 fn test_protocol_config_validate_pending_output_limit_too_low() {
1885 let config = ProtocolConfig {
1887 pending_output_limit: 0,
1888 ..ProtocolConfig::default()
1889 };
1890 let result = config.validate();
1891 assert!(result.is_err());
1892 let err = result.unwrap_err();
1893 assert!(matches!(
1894 err,
1895 FortressError::InvalidRequestStructured {
1896 kind: InvalidRequestKind::ConfigValueOutOfRange {
1897 field: "pending_output_limit",
1898 min: 1,
1899 max: 4096,
1900 ..
1901 }
1902 }
1903 ));
1904 }
1905
1906 #[test]
1907 fn test_protocol_config_validate_pending_output_limit_too_high() {
1908 let config = ProtocolConfig {
1910 pending_output_limit: 4097,
1911 ..ProtocolConfig::default()
1912 };
1913 let result = config.validate();
1914 assert!(result.is_err());
1915 let err = result.unwrap_err();
1916 assert!(matches!(
1917 err,
1918 FortressError::InvalidRequestStructured {
1919 kind: InvalidRequestKind::ConfigValueOutOfRange {
1920 field: "pending_output_limit",
1921 min: 1,
1922 max: 4096,
1923 ..
1924 }
1925 }
1926 ));
1927 }
1928
1929 #[test]
1930 fn test_protocol_config_validate_sync_retry_warning_threshold_valid() {
1931 let config = ProtocolConfig {
1933 sync_retry_warning_threshold: 1,
1934 ..ProtocolConfig::default()
1935 };
1936 config.validate().unwrap();
1937
1938 let config = ProtocolConfig {
1940 sync_retry_warning_threshold: 1000,
1941 ..ProtocolConfig::default()
1942 };
1943 config.validate().unwrap();
1944
1945 let config = ProtocolConfig {
1947 sync_retry_warning_threshold: 25,
1948 ..ProtocolConfig::default()
1949 };
1950 config.validate().unwrap();
1951 }
1952
1953 #[test]
1954 fn test_protocol_config_validate_sync_retry_warning_threshold_too_low() {
1955 let config = ProtocolConfig {
1957 sync_retry_warning_threshold: 0,
1958 ..ProtocolConfig::default()
1959 };
1960 let result = config.validate();
1961 assert!(result.is_err());
1962 let err = result.unwrap_err();
1963 assert!(matches!(
1964 err,
1965 FortressError::InvalidRequestStructured {
1966 kind: InvalidRequestKind::ConfigValueOutOfRange {
1967 field: "sync_retry_warning_threshold",
1968 min: 1,
1969 max: 1000,
1970 ..
1971 }
1972 }
1973 ));
1974 }
1975
1976 #[test]
1977 fn test_protocol_config_validate_sync_retry_warning_threshold_too_high() {
1978 let config = ProtocolConfig {
1980 sync_retry_warning_threshold: 1001,
1981 ..ProtocolConfig::default()
1982 };
1983 let result = config.validate();
1984 assert!(result.is_err());
1985 let err = result.unwrap_err();
1986 assert!(matches!(
1987 err,
1988 FortressError::InvalidRequestStructured {
1989 kind: InvalidRequestKind::ConfigValueOutOfRange {
1990 field: "sync_retry_warning_threshold",
1991 min: 1,
1992 max: 1000,
1993 ..
1994 }
1995 }
1996 ));
1997 }
1998
1999 #[test]
2000 fn test_protocol_config_validate_sync_duration_warning_ms_valid() {
2001 let config = ProtocolConfig {
2003 sync_duration_warning_ms: 1,
2004 ..ProtocolConfig::default()
2005 };
2006 config.validate().unwrap();
2007
2008 let config = ProtocolConfig {
2010 sync_duration_warning_ms: 300000,
2011 ..ProtocolConfig::default()
2012 };
2013 config.validate().unwrap();
2014
2015 let config = ProtocolConfig {
2017 sync_duration_warning_ms: 5000,
2018 ..ProtocolConfig::default()
2019 };
2020 config.validate().unwrap();
2021 }
2022
2023 #[test]
2024 fn test_protocol_config_validate_sync_duration_warning_ms_too_low() {
2025 let config = ProtocolConfig {
2027 sync_duration_warning_ms: 0,
2028 ..ProtocolConfig::default()
2029 };
2030 let result = config.validate();
2031 assert!(result.is_err());
2032 let err = result.unwrap_err();
2033 assert!(matches!(
2034 err,
2035 FortressError::InvalidRequestStructured {
2036 kind: InvalidRequestKind::ConfigValueOutOfRange {
2037 field: "sync_duration_warning_ms",
2038 min: 1,
2039 max: 300000,
2040 ..
2041 }
2042 }
2043 ));
2044 }
2045
2046 #[test]
2047 fn test_protocol_config_validate_sync_duration_warning_ms_too_high() {
2048 let config = ProtocolConfig {
2050 sync_duration_warning_ms: 300001,
2051 ..ProtocolConfig::default()
2052 };
2053 let result = config.validate();
2054 assert!(result.is_err());
2055 let err = result.unwrap_err();
2056 assert!(matches!(
2057 err,
2058 FortressError::InvalidRequestStructured {
2059 kind: InvalidRequestKind::ConfigValueOutOfRange {
2060 field: "sync_duration_warning_ms",
2061 min: 1,
2062 max: 300000,
2063 ..
2064 }
2065 }
2066 ));
2067 }
2068
2069 #[test]
2070 fn test_protocol_config_validate_input_history_multiplier_valid() {
2071 let config = ProtocolConfig {
2073 input_history_multiplier: 1,
2074 ..ProtocolConfig::default()
2075 };
2076 config.validate().unwrap();
2077
2078 let config = ProtocolConfig {
2080 input_history_multiplier: 16,
2081 ..ProtocolConfig::default()
2082 };
2083 config.validate().unwrap();
2084
2085 let config = ProtocolConfig {
2087 input_history_multiplier: 4,
2088 ..ProtocolConfig::default()
2089 };
2090 config.validate().unwrap();
2091 }
2092
2093 #[test]
2094 fn test_protocol_config_validate_input_history_multiplier_too_low() {
2095 let config = ProtocolConfig {
2097 input_history_multiplier: 0,
2098 ..ProtocolConfig::default()
2099 };
2100 let result = config.validate();
2101 assert!(result.is_err());
2102 let err = result.unwrap_err();
2103 assert!(matches!(
2104 err,
2105 FortressError::InvalidRequestStructured {
2106 kind: InvalidRequestKind::ConfigValueOutOfRange {
2107 field: "input_history_multiplier",
2108 min: 1,
2109 max: 16,
2110 ..
2111 }
2112 }
2113 ));
2114 }
2115
2116 #[test]
2117 fn test_protocol_config_validate_input_history_multiplier_too_high() {
2118 let config = ProtocolConfig {
2120 input_history_multiplier: 17,
2121 ..ProtocolConfig::default()
2122 };
2123 let result = config.validate();
2124 assert!(result.is_err());
2125 let err = result.unwrap_err();
2126 assert!(matches!(
2127 err,
2128 FortressError::InvalidRequestStructured {
2129 kind: InvalidRequestKind::ConfigValueOutOfRange {
2130 field: "input_history_multiplier",
2131 min: 1,
2132 max: 16,
2133 ..
2134 }
2135 }
2136 ));
2137 }
2138
2139 #[test]
2140 fn test_protocol_config_validate_multiple_invalid_fields() {
2141 let config = ProtocolConfig {
2144 quality_report_interval: Duration::from_millis(0), shutdown_delay: Duration::from_millis(0), max_checksum_history: 0, ..ProtocolConfig::default()
2148 };
2149 let result = config.validate();
2150 assert!(result.is_err());
2151 let err = result.unwrap_err();
2152 assert!(matches!(
2154 err,
2155 FortressError::InvalidRequestStructured {
2156 kind: InvalidRequestKind::DurationConfigOutOfRange {
2157 field: "quality_report_interval",
2158 ..
2159 }
2160 }
2161 ));
2162 }
2163
2164 #[test]
2165 fn test_protocol_config_validate_all_fields_at_boundaries() {
2166 let config = ProtocolConfig {
2168 quality_report_interval: Duration::from_millis(1),
2169 shutdown_delay: Duration::from_millis(1),
2170 max_checksum_history: 1,
2171 pending_output_limit: 1,
2172 sync_retry_warning_threshold: 1,
2173 sync_duration_warning_ms: 1,
2174 input_history_multiplier: 1,
2175 protocol_rng_seed: None,
2176 };
2177 config.validate().unwrap();
2178
2179 let config = ProtocolConfig {
2181 quality_report_interval: Duration::from_millis(10000),
2182 shutdown_delay: Duration::from_millis(300000),
2183 max_checksum_history: 1024,
2184 pending_output_limit: 4096,
2185 sync_retry_warning_threshold: 1000,
2186 sync_duration_warning_ms: 300000,
2187 input_history_multiplier: 16,
2188 protocol_rng_seed: None,
2189 };
2190 config.validate().unwrap();
2191 }
2192
2193 #[test]
2198 fn test_protocol_config_deterministic_preset() {
2199 let config = ProtocolConfig::deterministic(12345);
2200 assert_eq!(config.protocol_rng_seed, Some(12345));
2201 assert_eq!(
2203 config.quality_report_interval,
2204 ProtocolConfig::default().quality_report_interval
2205 );
2206 }
2207
2208 #[test]
2209 fn test_protocol_config_deterministic_different_seeds() {
2210 let config1 = ProtocolConfig::deterministic(1);
2211 let config2 = ProtocolConfig::deterministic(2);
2212 assert_ne!(config1.protocol_rng_seed, config2.protocol_rng_seed);
2213 }
2214
2215 #[test]
2216 fn test_protocol_config_default_has_no_seed() {
2217 let config = ProtocolConfig::default();
2218 assert_eq!(config.protocol_rng_seed, None);
2219 }
2220
2221 #[test]
2222 fn test_protocol_config_all_presets_have_no_seed() {
2223 assert_eq!(ProtocolConfig::competitive().protocol_rng_seed, None);
2225 assert_eq!(ProtocolConfig::high_latency().protocol_rng_seed, None);
2226 assert_eq!(ProtocolConfig::debug().protocol_rng_seed, None);
2227 assert_eq!(ProtocolConfig::mobile().protocol_rng_seed, None);
2228 }
2229
2230 #[test]
2231 fn test_protocol_config_seed_validates_ok() {
2232 let config = ProtocolConfig::deterministic(42);
2234 config.validate().unwrap();
2235 }
2236
2237 #[test]
2242 fn spectator_config_default_values() {
2243 let config = SpectatorConfig::default();
2244 assert_eq!(config.buffer_size, 60);
2245 assert_eq!(config.catchup_speed, 1);
2246 assert_eq!(config.max_frames_behind, 10);
2247 }
2248
2249 #[test]
2250 fn spectator_config_new_equals_default() {
2251 let new_config = SpectatorConfig::new();
2252 let default_config = SpectatorConfig::default();
2253 assert_eq!(new_config, default_config);
2254 }
2255
2256 #[test]
2257 fn spectator_config_fast_paced_preset() {
2258 let config = SpectatorConfig::fast_paced();
2259 assert_eq!(config.buffer_size, 90);
2260 assert_eq!(config.catchup_speed, 2);
2261 assert_eq!(config.max_frames_behind, 15);
2262 }
2263
2264 #[test]
2265 fn spectator_config_slow_connection_preset() {
2266 let config = SpectatorConfig::slow_connection();
2267 assert_eq!(config.buffer_size, 120);
2268 assert_eq!(config.catchup_speed, 1);
2269 assert_eq!(config.max_frames_behind, 20);
2270 }
2271
2272 #[test]
2273 fn spectator_config_local_preset() {
2274 let config = SpectatorConfig::local();
2275 assert_eq!(config.buffer_size, 30);
2276 assert_eq!(config.catchup_speed, 2);
2277 assert_eq!(config.max_frames_behind, 5);
2278 }
2279
2280 #[test]
2281 fn spectator_config_broadcast_preset() {
2282 let config = SpectatorConfig::broadcast();
2283 assert_eq!(config.buffer_size, 180);
2284 assert_eq!(config.catchup_speed, 1);
2285 assert_eq!(config.max_frames_behind, 30);
2286 }
2287
2288 #[test]
2289 fn spectator_config_mobile_preset() {
2290 let config = SpectatorConfig::mobile();
2291 assert_eq!(config.buffer_size, 120);
2292 assert_eq!(config.catchup_speed, 1);
2293 assert_eq!(config.max_frames_behind, 25);
2294 }
2295
2296 #[test]
2297 fn spectator_config_display() {
2298 let config = SpectatorConfig::default();
2299 let display_str = config.to_string();
2300 assert!(display_str.contains("SpectatorConfig"));
2301 assert!(display_str.contains("buffer: 60"));
2302 assert!(display_str.contains("catchup_speed: 1"));
2303 assert!(display_str.contains("max_behind: 10"));
2304
2305 let config = SpectatorConfig::broadcast();
2306 let display_str = config.to_string();
2307 assert!(display_str.contains("buffer: 180"));
2308 assert!(display_str.contains("max_behind: 30"));
2309 }
2310
2311 #[test]
2312 fn spectator_config_equality() {
2313 let config1 = SpectatorConfig::default();
2314 let config2 = SpectatorConfig::default();
2315 let config3 = SpectatorConfig::broadcast();
2316 assert_eq!(config1, config2);
2317 assert_ne!(config1, config3);
2318 }
2319
2320 #[test]
2321 #[allow(clippy::clone_on_copy)]
2322 fn spectator_config_clone() {
2323 let config = SpectatorConfig::fast_paced();
2324 let cloned = config.clone();
2325 assert_eq!(config, cloned);
2326 }
2327
2328 #[test]
2329 fn spectator_config_copy() {
2330 let config = SpectatorConfig::local();
2331 let copied: SpectatorConfig = config;
2332 assert_eq!(config, copied);
2333 }
2334
2335 #[test]
2336 fn spectator_config_debug_format() {
2337 let config = SpectatorConfig::default();
2338 let debug_str = format!("{:?}", config);
2339 assert!(debug_str.contains("SpectatorConfig"));
2340 assert!(debug_str.contains("buffer_size"));
2341 assert!(debug_str.contains("catchup_speed"));
2342 }
2343}
2344
2345#[cfg(kani)]
2358mod kani_config_proofs {
2359 use super::*;
2360
2361 #[kani::proof]
2370 #[kani::unwind(2)]
2371 fn proof_validate_accepts_valid_queue_lengths() {
2372 let queue_length: usize = kani::any();
2373 kani::assume(queue_length <= 512);
2375
2376 let config = InputQueueConfig { queue_length };
2377 let result = config.validate();
2378
2379 if queue_length >= 2 {
2380 kani::assert(result.is_ok(), "validate() should accept queue_length >= 2");
2381 } else {
2382 kani::assert(result.is_err(), "validate() should reject queue_length < 2");
2383 }
2384 }
2385
2386 #[kani::proof]
2394 #[kani::unwind(2)]
2395 fn proof_validate_boundary_at_two() {
2396 let config_one = InputQueueConfig { queue_length: 1 };
2398 kani::assert(
2399 config_one.validate().is_err(),
2400 "queue_length=1 should be invalid",
2401 );
2402
2403 let config_two = InputQueueConfig { queue_length: 2 };
2405 kani::assert(
2406 config_two.validate().is_ok(),
2407 "queue_length=2 should be valid",
2408 );
2409 }
2410
2411 #[kani::proof]
2420 #[kani::unwind(10)]
2421 fn proof_validate_frame_delay_constraint() {
2422 let queue_length: usize = kani::any();
2423 let frame_delay: usize = kani::any();
2424
2425 kani::assume(queue_length >= 2 && queue_length <= 256);
2427 kani::assume(frame_delay <= 256);
2428
2429 let config = InputQueueConfig { queue_length };
2430 let result = config.validate_frame_delay(frame_delay);
2431
2432 if frame_delay < queue_length {
2433 kani::assert(
2434 result.is_ok(),
2435 "validate_frame_delay should accept delay < queue_length",
2436 );
2437 } else {
2438 kani::assert(
2439 result.is_err(),
2440 "validate_frame_delay should reject delay >= queue_length",
2441 );
2442 }
2443 }
2444
2445 #[kani::proof]
2454 #[kani::unwind(2)]
2455 fn proof_max_frame_delay_derivation() {
2456 let queue_length: usize = kani::any();
2457 kani::assume(queue_length <= 512);
2458
2459 let config = InputQueueConfig { queue_length };
2460 let max_delay = config.max_frame_delay();
2461
2462 let expected = queue_length.saturating_sub(1);
2464 kani::assert(
2465 max_delay == expected,
2466 "max_frame_delay should equal queue_length.saturating_sub(1)",
2467 );
2468 }
2469
2470 #[kani::proof]
2479 #[kani::unwind(2)]
2480 fn proof_max_frame_delay_is_valid_delay() {
2481 let queue_length: usize = kani::any();
2482 kani::assume(queue_length >= 2 && queue_length <= 256);
2483
2484 let config = InputQueueConfig { queue_length };
2485 let max_delay = config.max_frame_delay();
2486 let result = config.validate_frame_delay(max_delay);
2487
2488 kani::assert(
2489 result.is_ok(),
2490 "max_frame_delay() should always be a valid frame_delay for valid configs",
2491 );
2492 }
2493
2494 #[kani::proof]
2503 #[kani::unwind(2)]
2504 fn proof_all_presets_valid() {
2505 let standard = InputQueueConfig::standard();
2506 let high_latency = InputQueueConfig::high_latency();
2507 let minimal = InputQueueConfig::minimal();
2508
2509 kani::assert(
2510 standard.validate().is_ok(),
2511 "standard() preset should be valid",
2512 );
2513 kani::assert(
2514 high_latency.validate().is_ok(),
2515 "high_latency() preset should be valid",
2516 );
2517 kani::assert(
2518 minimal.validate().is_ok(),
2519 "minimal() preset should be valid",
2520 );
2521 }
2522
2523 #[kani::proof]
2534 #[kani::unwind(2)]
2535 fn proof_preset_values() {
2536 let standard = InputQueueConfig::standard();
2537 let high_latency = InputQueueConfig::high_latency();
2538 let minimal = InputQueueConfig::minimal();
2539
2540 kani::assert(
2542 standard.queue_length == INPUT_QUEUE_LENGTH,
2543 "standard() should have queue_length=INPUT_QUEUE_LENGTH",
2544 );
2545 kani::assert(
2547 high_latency.queue_length == 256,
2548 "high_latency() should have queue_length=256",
2549 );
2550 kani::assert(
2552 minimal.queue_length == 32,
2553 "minimal() should have queue_length=32",
2554 );
2555 }
2556}