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
default_possible_settings() -> Vec<MediaTrackSettings>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
default_supported_constraints() -> MediaTrackSupportedConstraints84 fn default_supported_constraints() -> MediaTrackSupportedConstraints {
85 MediaTrackSupportedConstraints::from_iter(all_properties().into_iter().cloned())
86 }
87
test_overconstrained( possible_settings: &[MediaTrackSettings], mandatory_constraints: ResolvedMandatoryMediaTrackConstraints, exposure_mode: DeviceInformationExposureMode, ) -> OverconstrainedError88 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
test_constrained( possible_settings: &[MediaTrackSettings], mandatory_constraints: ResolvedMandatoryMediaTrackConstraints, advanced_constraints: ResolvedAdvancedMediaTrackConstraints, ) -> Vec<&MediaTrackSettings>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
default_constraints() -> MediaTrackConstraints131 fn default_constraints() -> MediaTrackConstraints {
132 MediaTrackConstraints {
133 mandatory: MandatoryMediaTrackConstraints::default(),
134 advanced: AdvancedMediaTrackConstraints::default(),
135 }
136 }
137
default_resolved_constraints() -> ResolvedMediaTrackConstraints138 fn default_resolved_constraints() -> ResolvedMediaTrackConstraints {
139 default_constraints().into_resolved()
140 }
141
default_sanitized_constraints() -> SanitizedMediaTrackConstraints142 fn default_sanitized_constraints() -> SanitizedMediaTrackConstraints {
143 default_resolved_constraints().into_sanitized(&default_supported_constraints())
144 }
145
146 #[test]
pass_through()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]
protected()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]
missing()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]
mismatch()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]
too_small()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]
too_large()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]
specific_device_id()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]
value()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]
value_range()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]
value_sequence()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]
value()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]
value_range()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]
value_sequence()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]
native()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]
macros()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]
json()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