1 use std::iter::FromIterator; 2 3 use lazy_static::lazy_static; 4 5 use crate::{ 6 algorithms::{select_settings_candidates, SelectSettingsError}, 7 errors::OverconstrainedError, 8 property::all::{name::*, names as all_properties}, 9 AdvancedMediaTrackConstraints, FacingMode, MandatoryMediaTrackConstraints, 10 MediaTrackConstraints, MediaTrackSettings, MediaTrackSupportedConstraints, ResizeMode, 11 ResolvedAdvancedMediaTrackConstraints, ResolvedMandatoryMediaTrackConstraints, 12 ResolvedMediaTrackConstraint, ResolvedMediaTrackConstraints, ResolvedValueConstraint, 13 ResolvedValueRangeConstraint, ResolvedValueSequenceConstraint, SanitizedMediaTrackConstraints, 14 }; 15 16 use super::DeviceInformationExposureMode; 17 18 lazy_static! { 19 static ref VIDEO_IDEAL: MediaTrackSettings = MediaTrackSettings::from_iter([ 20 (&ASPECT_RATIO, 0.5625.into()), 21 (&FACING_MODE, FacingMode::user().into()), 22 (&FRAME_RATE, 60.0.into()), 23 (&WIDTH, 1920.into()), 24 (&HEIGHT, 1080.into()), 25 (&RESIZE_MODE, ResizeMode::none().into()), 26 ]); 27 static ref VIDEO_480P: MediaTrackSettings = MediaTrackSettings::from_iter([ 28 (&DEVICE_ID, "480p".into()), 29 (&ASPECT_RATIO, 0.5625.into()), 30 (&FACING_MODE, FacingMode::user().into()), 31 (&FRAME_RATE, 240.into()), 32 (&WIDTH, 720.into()), 33 (&HEIGHT, 480.into()), 34 (&RESIZE_MODE, ResizeMode::crop_and_scale().into()), 35 ]); 36 static ref VIDEO_720P: MediaTrackSettings = MediaTrackSettings::from_iter([ 37 (&DEVICE_ID, "720p".into()), 38 (&ASPECT_RATIO, 0.5625.into()), 39 (&FACING_MODE, FacingMode::user().into()), 40 (&FRAME_RATE, 120.into()), 41 (&WIDTH, 1280.into()), 42 (&HEIGHT, 720.into()), 43 (&RESIZE_MODE, ResizeMode::crop_and_scale().into()), 44 ]); 45 static ref VIDEO_1080P: MediaTrackSettings = MediaTrackSettings::from_iter([ 46 (&DEVICE_ID, "1080p".into()), 47 (&ASPECT_RATIO, 0.5625.into()), 48 (&FACING_MODE, FacingMode::user().into()), 49 (&FRAME_RATE, 60.into()), 50 (&WIDTH, 1920.into()), 51 (&HEIGHT, 1080.into()), 52 (&RESIZE_MODE, ResizeMode::none().into()), 53 ]); 54 static ref VIDEO_1440P: MediaTrackSettings = MediaTrackSettings::from_iter([ 55 (&DEVICE_ID, "1440p".into()), 56 (&ASPECT_RATIO, 0.5625.into()), 57 (&FACING_MODE, FacingMode::user().into()), 58 (&FRAME_RATE, 30.into()), 59 (&WIDTH, 2560.into()), 60 (&HEIGHT, 1440.into()), 61 (&RESIZE_MODE, ResizeMode::none().into()), 62 ]); 63 static ref VIDEO_2160P: MediaTrackSettings = MediaTrackSettings::from_iter([ 64 (&DEVICE_ID, "2160p".into()), 65 (&ASPECT_RATIO, 0.5625.into()), 66 (&FACING_MODE, FacingMode::user().into()), 67 (&FRAME_RATE, 15.into()), 68 (&WIDTH, 3840.into()), 69 (&HEIGHT, 2160.into()), 70 (&RESIZE_MODE, ResizeMode::none().into()), 71 ]); 72 } 73 74 fn default_possible_settings() -> Vec<MediaTrackSettings> { 75 vec![ 76 VIDEO_480P.clone(), 77 VIDEO_720P.clone(), 78 VIDEO_1080P.clone(), 79 VIDEO_1440P.clone(), 80 VIDEO_2160P.clone(), 81 ] 82 } 83 84 fn default_supported_constraints() -> MediaTrackSupportedConstraints { 85 MediaTrackSupportedConstraints::from_iter(all_properties().into_iter().cloned()) 86 } 87 88 fn test_overconstrained( 89 possible_settings: &[MediaTrackSettings], 90 mandatory_constraints: ResolvedMandatoryMediaTrackConstraints, 91 exposure_mode: DeviceInformationExposureMode, 92 ) -> OverconstrainedError { 93 let constraints = ResolvedMediaTrackConstraints { 94 mandatory: mandatory_constraints, 95 advanced: ResolvedAdvancedMediaTrackConstraints::default(), 96 } 97 .to_sanitized(&default_supported_constraints()); 98 99 let result = select_settings_candidates(possible_settings.iter(), &constraints, exposure_mode); 100 101 let actual = result.err().unwrap(); 102 103 let SelectSettingsError::Overconstrained(overconstrained_error) = actual; 104 105 overconstrained_error 106 } 107 108 fn test_constrained( 109 possible_settings: &[MediaTrackSettings], 110 mandatory_constraints: ResolvedMandatoryMediaTrackConstraints, 111 advanced_constraints: ResolvedAdvancedMediaTrackConstraints, 112 ) -> Vec<&MediaTrackSettings> { 113 let constraints = ResolvedMediaTrackConstraints { 114 mandatory: mandatory_constraints, 115 advanced: advanced_constraints, 116 } 117 .to_sanitized(&default_supported_constraints()); 118 119 let result = select_settings_candidates( 120 possible_settings.iter(), 121 &constraints, 122 DeviceInformationExposureMode::Exposed, 123 ); 124 125 result.unwrap() 126 } 127 128 mod unconstrained { 129 use super::*; 130 131 fn default_constraints() -> MediaTrackConstraints { 132 MediaTrackConstraints { 133 mandatory: MandatoryMediaTrackConstraints::default(), 134 advanced: AdvancedMediaTrackConstraints::default(), 135 } 136 } 137 138 fn default_resolved_constraints() -> ResolvedMediaTrackConstraints { 139 default_constraints().into_resolved() 140 } 141 142 fn default_sanitized_constraints() -> SanitizedMediaTrackConstraints { 143 default_resolved_constraints().into_sanitized(&default_supported_constraints()) 144 } 145 146 #[test] 147 fn pass_through() { 148 let possible_settings = default_possible_settings(); 149 let sanitized_constraints = default_sanitized_constraints(); 150 151 let actual = select_settings_candidates( 152 &possible_settings[..], 153 &sanitized_constraints, 154 DeviceInformationExposureMode::Exposed, 155 ) 156 .unwrap(); 157 let expected: Vec<_> = possible_settings.iter().collect(); 158 159 assert_eq!(actual, expected); 160 } 161 } 162 163 mod overconstrained { 164 use crate::MediaTrackProperty; 165 166 use super::*; 167 168 #[test] 169 fn protected() { 170 let error = test_overconstrained( 171 &default_possible_settings(), 172 ResolvedMandatoryMediaTrackConstraints::from_iter([( 173 GROUP_ID.clone(), 174 ResolvedValueConstraint::default() 175 .exact("missing-group".to_owned()) 176 .into(), 177 )]), 178 DeviceInformationExposureMode::Protected, 179 ); 180 181 assert_eq!(error.constraint, MediaTrackProperty::from("")); 182 assert_eq!(error.message, None); 183 } 184 185 mod exposed { 186 use super::*; 187 188 #[test] 189 fn missing() { 190 let error = test_overconstrained( 191 &default_possible_settings(), 192 ResolvedMandatoryMediaTrackConstraints::from_iter([( 193 GROUP_ID.clone(), 194 ResolvedValueConstraint::default() 195 .exact("missing-group".to_owned()) 196 .into(), 197 )]), 198 DeviceInformationExposureMode::Exposed, 199 ); 200 201 let constraint = &error.constraint; 202 let err_message = error.message.as_ref().expect("Error message."); 203 204 assert_eq!(constraint, &GROUP_ID); 205 assert_eq!( 206 err_message, 207 "Setting was missing (does not satisfy (x == \"missing-group\"))." 208 ); 209 } 210 211 #[test] 212 fn mismatch() { 213 let error = test_overconstrained( 214 &default_possible_settings(), 215 ResolvedMandatoryMediaTrackConstraints::from_iter([( 216 DEVICE_ID.clone(), 217 ResolvedValueConstraint::default() 218 .exact("mismatched-device".to_owned()) 219 .into(), 220 )]), 221 DeviceInformationExposureMode::Exposed, 222 ); 223 224 let constraint = &error.constraint; 225 let err_message = error.message.as_ref().expect("Error message."); 226 227 assert_eq!(constraint, &DEVICE_ID); 228 assert_eq!( 229 err_message, 230 "Setting was a mismatch ([\"1080p\", \"1440p\", \"2160p\", \"480p\", \"720p\"] do not satisfy (x == \"mismatched-device\"))." 231 ); 232 } 233 234 #[test] 235 fn too_small() { 236 let error = test_overconstrained( 237 &default_possible_settings(), 238 ResolvedMandatoryMediaTrackConstraints::from_iter([( 239 FRAME_RATE.clone(), 240 ResolvedValueRangeConstraint::default().min(1000).into(), 241 )]), 242 DeviceInformationExposureMode::Exposed, 243 ); 244 245 let constraint = &error.constraint; 246 let err_message = error.message.as_ref().expect("Error message."); 247 248 assert_eq!(constraint, &FRAME_RATE); 249 assert_eq!( 250 err_message, 251 "Setting was too small ([120, 15, 240, 30, 60] do not satisfy (1000 <= x))." 252 ); 253 } 254 255 #[test] 256 fn too_large() { 257 let error = test_overconstrained( 258 &default_possible_settings(), 259 ResolvedMandatoryMediaTrackConstraints::from_iter([( 260 FRAME_RATE.clone(), 261 ResolvedValueRangeConstraint::default().max(10).into(), 262 )]), 263 DeviceInformationExposureMode::Exposed, 264 ); 265 266 let constraint = &error.constraint; 267 let err_message = error.message.as_ref().expect("Error message."); 268 269 assert_eq!(constraint, &FRAME_RATE); 270 assert_eq!( 271 err_message, 272 "Setting was too large ([120, 15, 240, 30, 60] do not satisfy (x <= 10))." 273 ); 274 } 275 } 276 } 277 278 mod constrained { 279 use super::*; 280 281 #[test] 282 fn specific_device_id() { 283 let possible_settings = default_possible_settings(); 284 285 for target_settings in possible_settings.iter() { 286 let setting = match target_settings.get(&DEVICE_ID) { 287 Some(setting) => setting, 288 None => continue, 289 }; 290 291 let actual = test_constrained( 292 &possible_settings, 293 ResolvedMandatoryMediaTrackConstraints::from_iter([( 294 DEVICE_ID.clone(), 295 ResolvedMediaTrackConstraint::exact_from(setting.clone()), 296 )]), 297 ResolvedAdvancedMediaTrackConstraints::default(), 298 ); 299 300 let expected = vec![target_settings]; 301 302 assert_eq!(actual, expected); 303 } 304 } 305 306 mod exact { 307 use super::*; 308 309 #[test] 310 fn value() { 311 let possible_settings = vec![ 312 MediaTrackSettings::from_iter([ 313 (&DEVICE_ID, "a".into()), 314 (&GROUP_ID, "group-0".into()), 315 ]), 316 MediaTrackSettings::from_iter([ 317 (&DEVICE_ID, "b".into()), 318 (&GROUP_ID, "group-1".into()), 319 ]), 320 MediaTrackSettings::from_iter([ 321 (&DEVICE_ID, "c".into()), 322 (&GROUP_ID, "group-2".into()), 323 ]), 324 ]; 325 326 let actual = test_constrained( 327 &possible_settings, 328 ResolvedMandatoryMediaTrackConstraints::from_iter([( 329 &GROUP_ID, 330 ResolvedValueConstraint::default() 331 .exact("group-1".to_owned()) 332 .into(), 333 )]), 334 ResolvedAdvancedMediaTrackConstraints::default(), 335 ); 336 337 let expected = vec![&possible_settings[1]]; 338 339 assert_eq!(actual, expected); 340 } 341 342 #[test] 343 fn value_range() { 344 let possible_settings = vec![ 345 MediaTrackSettings::from_iter([(&DEVICE_ID, "a".into()), (&FRAME_RATE, 15.into())]), 346 MediaTrackSettings::from_iter([(&DEVICE_ID, "b".into()), (&FRAME_RATE, 30.into())]), 347 MediaTrackSettings::from_iter([(&DEVICE_ID, "c".into()), (&FRAME_RATE, 60.into())]), 348 ]; 349 350 let actual = test_constrained( 351 &possible_settings, 352 ResolvedMandatoryMediaTrackConstraints::from_iter([( 353 &FRAME_RATE, 354 ResolvedValueRangeConstraint::default().exact(30).into(), 355 )]), 356 ResolvedAdvancedMediaTrackConstraints::default(), 357 ); 358 359 let expected = vec![&possible_settings[1]]; 360 361 assert_eq!(actual, expected); 362 } 363 364 #[test] 365 fn value_sequence() { 366 let possible_settings = vec![ 367 MediaTrackSettings::from_iter([ 368 (&DEVICE_ID, "a".into()), 369 (&GROUP_ID, "group-0".into()), 370 ]), 371 MediaTrackSettings::from_iter([ 372 (&DEVICE_ID, "b".into()), 373 (&GROUP_ID, "group-1".into()), 374 ]), 375 MediaTrackSettings::from_iter([ 376 (&DEVICE_ID, "c".into()), 377 (&GROUP_ID, "group-2".into()), 378 ]), 379 ]; 380 381 let actual = test_constrained( 382 &possible_settings, 383 ResolvedMandatoryMediaTrackConstraints::from_iter([( 384 &GROUP_ID, 385 ResolvedValueSequenceConstraint::default() 386 .exact(vec!["group-1".to_owned(), "group-3".to_owned()]) 387 .into(), 388 )]), 389 ResolvedAdvancedMediaTrackConstraints::default(), 390 ); 391 392 let expected = vec![&possible_settings[1]]; 393 394 assert_eq!(actual, expected); 395 } 396 } 397 398 mod ideal { 399 use super::*; 400 401 #[test] 402 fn value() { 403 let possible_settings = vec![ 404 MediaTrackSettings::from_iter([ 405 (&DEVICE_ID, "a".into()), 406 (&GROUP_ID, "group-0".into()), 407 ]), 408 MediaTrackSettings::from_iter([ 409 (&DEVICE_ID, "b".into()), 410 (&GROUP_ID, "group-1".into()), 411 ]), 412 MediaTrackSettings::from_iter([ 413 (&DEVICE_ID, "c".into()), 414 (&GROUP_ID, "group-2".into()), 415 ]), 416 ]; 417 418 let actual = test_constrained( 419 &possible_settings, 420 ResolvedMandatoryMediaTrackConstraints::from_iter([( 421 &GROUP_ID, 422 ResolvedValueConstraint::default() 423 .ideal("group-1".to_owned()) 424 .into(), 425 )]), 426 ResolvedAdvancedMediaTrackConstraints::default(), 427 ); 428 429 let expected = vec![&possible_settings[1]]; 430 431 assert_eq!(actual, expected); 432 } 433 434 #[test] 435 fn value_range() { 436 let possible_settings = vec![ 437 MediaTrackSettings::from_iter([(&DEVICE_ID, "a".into()), (&FRAME_RATE, 15.into())]), 438 MediaTrackSettings::from_iter([(&DEVICE_ID, "b".into()), (&FRAME_RATE, 30.into())]), 439 MediaTrackSettings::from_iter([(&DEVICE_ID, "c".into()), (&FRAME_RATE, 60.into())]), 440 ]; 441 442 let actual = test_constrained( 443 &possible_settings, 444 ResolvedMandatoryMediaTrackConstraints::from_iter([( 445 &FRAME_RATE, 446 ResolvedValueRangeConstraint::default().ideal(32).into(), 447 )]), 448 ResolvedAdvancedMediaTrackConstraints::default(), 449 ); 450 451 let expected = vec![&possible_settings[1]]; 452 453 assert_eq!(actual, expected); 454 } 455 456 #[test] 457 fn value_sequence() { 458 let possible_settings = vec![ 459 MediaTrackSettings::from_iter([ 460 (&DEVICE_ID, "a".into()), 461 (&GROUP_ID, "group-0".into()), 462 ]), 463 MediaTrackSettings::from_iter([ 464 (&DEVICE_ID, "b".into()), 465 (&GROUP_ID, "group-1".into()), 466 ]), 467 MediaTrackSettings::from_iter([ 468 (&DEVICE_ID, "c".into()), 469 (&GROUP_ID, "group-2".into()), 470 ]), 471 ]; 472 473 let actual = test_constrained( 474 &possible_settings, 475 ResolvedMandatoryMediaTrackConstraints::from_iter([( 476 &GROUP_ID, 477 ResolvedValueSequenceConstraint::default() 478 .ideal(vec!["group-1".to_owned(), "group-3".to_owned()]) 479 .into(), 480 )]), 481 ResolvedAdvancedMediaTrackConstraints::default(), 482 ); 483 484 let expected = vec![&possible_settings[1]]; 485 486 assert_eq!(actual, expected); 487 } 488 } 489 } 490 491 // ``` 492 // ┌ 493 // mandatory constraints: ┤ ┄───────────────────────────────────────────┤ 494 // └ 495 // ┌ 496 // advanced constraints: ┤ ├─┤ ├────────────────────────────┄ 497 // └ 498 // ┌ 499 // possible settings: ┤ ●─────────────●──────────────●──────────────●─────────────● 500 // └ 480p 720p 1080p 1440p 2160p 501 // └───────┬──────┘ 502 // selected settings: ─────────────────────────────────────────┘ 503 // ``` 504 mod smoke { 505 use crate::{MediaTrackConstraintSet, ValueConstraint, ValueRangeConstraint}; 506 507 use super::*; 508 509 #[test] 510 fn native() { 511 let supported_constraints = MediaTrackSupportedConstraints::from_iter(vec![ 512 &DEVICE_ID, 513 &HEIGHT, 514 &WIDTH, 515 &RESIZE_MODE, 516 ]); 517 518 let possible_settings = vec![ 519 MediaTrackSettings::from_iter([ 520 (&DEVICE_ID, "480p".into()), 521 (&HEIGHT, 480.into()), 522 (&WIDTH, 720.into()), 523 (&RESIZE_MODE, ResizeMode::crop_and_scale().into()), 524 ]), 525 MediaTrackSettings::from_iter([ 526 (&DEVICE_ID, "720p".into()), 527 (&HEIGHT, 720.into()), 528 (&WIDTH, 1280.into()), 529 (&RESIZE_MODE, ResizeMode::crop_and_scale().into()), 530 ]), 531 MediaTrackSettings::from_iter([ 532 (&DEVICE_ID, "1080p".into()), 533 (&HEIGHT, 1080.into()), 534 (&WIDTH, 1920.into()), 535 (&RESIZE_MODE, ResizeMode::none().into()), 536 ]), 537 MediaTrackSettings::from_iter([ 538 (&DEVICE_ID, "1440p".into()), 539 (&HEIGHT, 1440.into()), 540 (&WIDTH, 2560.into()), 541 (&RESIZE_MODE, ResizeMode::none().into()), 542 ]), 543 MediaTrackSettings::from_iter([ 544 (&DEVICE_ID, "2160p".into()), 545 (&HEIGHT, 2160.into()), 546 (&WIDTH, 3840.into()), 547 (&RESIZE_MODE, ResizeMode::none().into()), 548 ]), 549 ]; 550 551 let constraints = MediaTrackConstraints { 552 mandatory: MandatoryMediaTrackConstraints::from_iter([ 553 ( 554 &WIDTH, 555 ValueRangeConstraint::Constraint( 556 ResolvedValueRangeConstraint::default().max(2560), 557 ) 558 .into(), 559 ), 560 ( 561 &HEIGHT, 562 ValueRangeConstraint::Constraint( 563 ResolvedValueRangeConstraint::default().max(1440), 564 ) 565 .into(), 566 ), 567 // Unsupported constraint, which should thus get ignored: 568 ( 569 &FRAME_RATE, 570 ValueRangeConstraint::Constraint( 571 ResolvedValueRangeConstraint::default().exact(30.0), 572 ) 573 .into(), 574 ), 575 // Ideal resize-mode: 576 ( 577 &RESIZE_MODE, 578 ValueConstraint::Bare(ResizeMode::none()).into(), 579 ), 580 ]), 581 advanced: AdvancedMediaTrackConstraints::from_iter([ 582 // The first advanced constraint set of "exact 800p" does not match 583 // any candidate and should thus get ignored by the algorithm: 584 MediaTrackConstraintSet::from_iter([( 585 &HEIGHT, 586 ValueRangeConstraint::Constraint( 587 ResolvedValueRangeConstraint::default().exact(800), 588 ) 589 .into(), 590 )]), 591 // The second advanced constraint set of "no resizing" does match 592 // candidates and should thus be applied by the algorithm: 593 MediaTrackConstraintSet::from_iter([( 594 &RESIZE_MODE, 595 ValueConstraint::Constraint( 596 ResolvedValueConstraint::default().exact(ResizeMode::none()), 597 ) 598 .into(), 599 )]), 600 ]), 601 }; 602 603 // Resolve bare values to proper constraints: 604 let resolved_constraints = constraints.into_resolved(); 605 606 // Sanitize constraints, removing empty and unsupported constraints: 607 let sanitized_constraints = resolved_constraints.to_sanitized(&supported_constraints); 608 609 let actual = select_settings_candidates( 610 &possible_settings, 611 &sanitized_constraints, 612 DeviceInformationExposureMode::Exposed, 613 ) 614 .unwrap(); 615 616 let expected = vec![&possible_settings[2], &possible_settings[3]]; 617 618 assert_eq!(actual, expected); 619 } 620 621 #[test] 622 fn macros() { 623 use crate::macros::*; 624 625 let supported_constraints = MediaTrackSupportedConstraints::from_iter(vec![ 626 &DEVICE_ID, 627 &HEIGHT, 628 &WIDTH, 629 &RESIZE_MODE, 630 ]); 631 632 let possible_settings = vec![ 633 settings![ 634 &DEVICE_ID => "480p", 635 &HEIGHT => 480, 636 &WIDTH => 720, 637 &RESIZE_MODE => ResizeMode::crop_and_scale(), 638 ], 639 settings![ 640 &DEVICE_ID => "720p", 641 &HEIGHT => 720, 642 &WIDTH => 1280, 643 &RESIZE_MODE => ResizeMode::crop_and_scale(), 644 ], 645 settings![ 646 &DEVICE_ID => "1080p", 647 &HEIGHT => 1080, 648 &WIDTH => 1920, 649 &RESIZE_MODE => ResizeMode::none(), 650 ], 651 settings![ 652 &DEVICE_ID => "1440p", 653 &HEIGHT => 1440, 654 &WIDTH => 2560, 655 &RESIZE_MODE => ResizeMode::none(), 656 ], 657 settings![ 658 &DEVICE_ID => "2160p", 659 &HEIGHT => 2160, 660 &WIDTH => 3840, 661 &RESIZE_MODE => ResizeMode::none(), 662 ], 663 ]; 664 665 let constraints = constraints! { 666 mandatory: { 667 &WIDTH => value_range_constraint!{ 668 max: 2560 669 }, 670 &HEIGHT => value_range_constraint!{ 671 max: 1440 672 }, 673 // Unsupported constraint, which should thus get ignored: 674 &FRAME_RATE => value_range_constraint!{ 675 exact: 30.0 676 }, 677 }, 678 advanced: [ 679 // The first advanced constraint set of "exact 800p" does not match 680 // any candidate and should thus get ignored by the algorithm: 681 { 682 &HEIGHT => value_range_constraint!{ 683 exact: 800 684 } 685 }, 686 // The second advanced constraint set of "no resizing" does match 687 // candidates and should thus be applied by the algorithm: 688 { 689 &RESIZE_MODE => value_constraint!{ 690 exact: ResizeMode::none() 691 } 692 }, 693 ] 694 }; 695 696 // Resolve bare values to proper constraints: 697 let resolved_constraints = constraints.into_resolved(); 698 699 // Sanitize constraints, removing empty and unsupported constraints: 700 let sanitized_constraints = resolved_constraints.to_sanitized(&supported_constraints); 701 702 let actual = select_settings_candidates( 703 &possible_settings, 704 &sanitized_constraints, 705 DeviceInformationExposureMode::Exposed, 706 ) 707 .unwrap(); 708 709 let expected = vec![&possible_settings[2], &possible_settings[3]]; 710 711 assert_eq!(actual, expected); 712 } 713 714 #[cfg(feature = "serde")] 715 #[test] 716 fn json() { 717 let supported_constraints = MediaTrackSupportedConstraints::from_iter(vec![ 718 &DEVICE_ID, 719 &HEIGHT, 720 &WIDTH, 721 &RESIZE_MODE, 722 ]); 723 724 // Deserialize possible settings from JSON: 725 let possible_settings: Vec<MediaTrackSettings> = { 726 let json = serde_json::json!([ 727 { "deviceId": "480p", "width": 720, "height": 480, "resizeMode": "crop-and-scale" }, 728 { "deviceId": "720p", "width": 1280, "height": 720, "resizeMode": "crop-and-scale" }, 729 { "deviceId": "1080p", "width": 1920, "height": 1080, "resizeMode": "none" }, 730 { "deviceId": "1440p", "width": 2560, "height": 1440, "resizeMode": "none" }, 731 { "deviceId": "2160p", "width": 3840, "height": 2160, "resizeMode": "none" }, 732 ]); 733 serde_json::from_value(json).unwrap() 734 }; 735 736 // Deserialize constraints from JSON: 737 let constraints: MediaTrackConstraints = { 738 let json = serde_json::json!({ 739 "width": { 740 "max": 2560, 741 }, 742 "height": { 743 "max": 1440, 744 }, 745 // Unsupported constraint, which should thus get ignored: 746 "frameRate": { 747 "exact": 30.0 748 }, 749 // Ideal resize-mode: 750 "resizeMode": "none", 751 "advanced": [ 752 // The first advanced constraint set of "exact 800p" does not match 753 // any candidate and should thus get ignored by the algorithm: 754 { "height": 800 }, 755 // The second advanced constraint set of "no resizing" does match 756 // candidates and should thus be applied by the algorithm: 757 { "resizeMode": "none" }, 758 ] 759 }); 760 serde_json::from_value(json).unwrap() 761 }; 762 763 // Resolve bare values to proper constraints: 764 let resolved_constraints = constraints.into_resolved(); 765 766 // Sanitize constraints, removing empty and unsupported constraints: 767 let sanitized_constraints = resolved_constraints.into_sanitized(&supported_constraints); 768 769 let actual = select_settings_candidates( 770 &possible_settings, 771 &sanitized_constraints, 772 DeviceInformationExposureMode::Exposed, 773 ) 774 .unwrap(); 775 776 let expected = vec![&possible_settings[2], &possible_settings[3]]; 777 778 assert_eq!(actual, expected); 779 } 780 } 781