1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "cast/streaming/answer_messages.h"
6 
7 #include <chrono>
8 #include <utility>
9 
10 #include "gmock/gmock.h"
11 #include "gtest/gtest.h"
12 #include "util/chrono_helpers.h"
13 #include "util/json/json_serialization.h"
14 
15 namespace openscreen {
16 namespace cast {
17 
18 namespace {
19 
20 using ::testing::ElementsAre;
21 
22 // NOTE: the castMode property has been removed from the specification. We leave
23 // it here in the valid offer to ensure that its inclusion does not break
24 // parsing.
25 constexpr char kValidAnswerJson[] = R"({
26   "castMode": "mirroring",
27   "udpPort": 1234,
28   "sendIndexes": [1, 3],
29   "ssrcs": [1233324, 2234222],
30   "constraints": {
31     "audio": {
32       "maxSampleRate": 96000,
33       "maxChannels": 5,
34       "minBitRate": 32000,
35       "maxBitRate": 320000,
36       "maxDelay": 5000
37     },
38     "video": {
39       "maxPixelsPerSecond": 62208000,
40       "minDimensions": {
41         "width": 320,
42         "height": 180,
43         "frameRate": 0
44       },
45       "maxDimensions": {
46         "width": 1920,
47         "height": 1080,
48         "frameRate": "60"
49       },
50       "minBitRate": 300000,
51       "maxBitRate": 10000000,
52       "maxDelay": 5000
53     }
54   },
55   "display": {
56     "dimensions": {
57       "width": 1920,
58       "height": 1080,
59       "frameRate": "60000/1001"
60     },
61     "aspectRatio": "64:27",
62     "scaling": "sender"
63   },
64   "receiverRtcpEventLog": [0, 1],
65   "receiverRtcpDscp": [234, 567],
66   "receiverGetStatus": true,
67   "rtpExtensions": ["adaptive_playout_delay"]
68 })";
69 
70 const Answer kValidAnswer{
71     1234,                         // udp_port
72     std::vector<int>{1, 2, 3},    // send_indexes
73     std::vector<Ssrc>{123, 456},  // ssrcs
74     absl::optional<Constraints>(Constraints{
75         AudioConstraints{
76             96000,              // max_sample_rate
77             7,                  // max_channels
78             32000,              // min_bit_rate
79             96000,              // max_bit_rate
80             milliseconds(2000)  // max_delay
81         },                      // audio
82         VideoConstraints{
83             40000.0,  // max_pixels_per_second
84             absl::optional<Dimensions>(Dimensions{
85                 320,                        // width
86                 480,                        // height
87                 SimpleFraction{15000, 101}  // frame_rate
88             }),                             // min_dimensions
89             Dimensions{
90                 1920,                   // width
91                 1080,                   // height
92                 SimpleFraction{288, 2}  // frame_rate
93             },
94             300000,             // min_bit_rate
95             144000000,          // max_bit_rate
96             milliseconds(3000)  // max_delay
97         }                       // video
98     }),                         // constraints
99     absl::optional<DisplayDescription>(DisplayDescription{
100         absl::optional<Dimensions>(Dimensions{
101             640,                   // width
102             480,                   // height
103             SimpleFraction{30, 1}  // frame_rate
104         }),
105         absl::optional<AspectRatio>(AspectRatio{16, 9}),  // aspect_ratio
106         absl::optional<AspectRatioConstraint>(
107             AspectRatioConstraint::kFixed),  // scaling
108     }),
109     std::vector<int>{7, 8, 9},              // receiver_rtcp_event_log
110     std::vector<int>{11, 12, 13},           // receiver_rtcp_dscp
111     true,                                   // receiver_get_status
112     std::vector<std::string>{"foo", "bar"}  // rtp_extensions
113 };
114 
115 constexpr int kValidMaxPixelsPerSecond = 1920 * 1080 * 30;
116 constexpr Dimensions kValidDimensions{1920, 1080, SimpleFraction{60, 1}};
117 static const VideoConstraints kValidVideoConstraints{
118     kValidMaxPixelsPerSecond, absl::optional<Dimensions>(kValidDimensions),
119     kValidDimensions,         300 * 1000,
120     300 * 1000 * 1000,        milliseconds(3000)};
121 
122 constexpr AudioConstraints kValidAudioConstraints{123, 456, 300, 9920,
123                                                   milliseconds(123)};
124 
ExpectEqualsValidAnswerJson(const Answer & answer)125 void ExpectEqualsValidAnswerJson(const Answer& answer) {
126   EXPECT_EQ(1234, answer.udp_port);
127 
128   EXPECT_THAT(answer.send_indexes, ElementsAre(1, 3));
129   EXPECT_THAT(answer.ssrcs, ElementsAre(1233324u, 2234222u));
130   ASSERT_TRUE(answer.constraints.has_value());
131   const AudioConstraints& audio = answer.constraints->audio;
132   EXPECT_EQ(96000, audio.max_sample_rate);
133   EXPECT_EQ(5, audio.max_channels);
134   EXPECT_EQ(32000, audio.min_bit_rate);
135   EXPECT_EQ(320000, audio.max_bit_rate);
136   EXPECT_EQ(milliseconds{5000}, audio.max_delay);
137 
138   const VideoConstraints& video = answer.constraints->video;
139   EXPECT_EQ(62208000, video.max_pixels_per_second);
140   ASSERT_TRUE(video.min_dimensions.has_value());
141   EXPECT_EQ(320, video.min_dimensions->width);
142   EXPECT_EQ(180, video.min_dimensions->height);
143   EXPECT_EQ((SimpleFraction{0, 1}), video.min_dimensions->frame_rate);
144   EXPECT_EQ(1920, video.max_dimensions.width);
145   EXPECT_EQ(1080, video.max_dimensions.height);
146   EXPECT_EQ((SimpleFraction{60, 1}), video.max_dimensions.frame_rate);
147   EXPECT_EQ(300000, video.min_bit_rate);
148   EXPECT_EQ(10000000, video.max_bit_rate);
149   EXPECT_EQ(milliseconds{5000}, video.max_delay);
150 
151   ASSERT_TRUE(answer.display.has_value());
152   const DisplayDescription& display = answer.display.value();
153   ASSERT_TRUE(display.dimensions.has_value());
154   EXPECT_EQ(1920, display.dimensions->width);
155   EXPECT_EQ(1080, display.dimensions->height);
156   EXPECT_EQ((SimpleFraction{60000, 1001}), display.dimensions->frame_rate);
157   EXPECT_EQ((AspectRatio{64, 27}), display.aspect_ratio.value());
158   EXPECT_EQ(AspectRatioConstraint::kFixed,
159             display.aspect_ratio_constraint.value());
160 
161   EXPECT_THAT(answer.receiver_rtcp_event_log, ElementsAre(0, 1));
162   EXPECT_THAT(answer.receiver_rtcp_dscp, ElementsAre(234, 567));
163   EXPECT_TRUE(answer.supports_wifi_status_reporting);
164   EXPECT_THAT(answer.rtp_extensions, ElementsAre("adaptive_playout_delay"));
165 }
166 
ExpectFailureOnParse(absl::string_view raw_json)167 void ExpectFailureOnParse(absl::string_view raw_json) {
168   ErrorOr<Json::Value> root = json::Parse(raw_json);
169   // Must be a valid JSON object, but not a valid answer.
170   ASSERT_TRUE(root.is_value());
171 
172   Answer answer;
173   EXPECT_FALSE(Answer::ParseAndValidate(std::move(root.value()), &answer));
174   EXPECT_FALSE(answer.IsValid());
175 }
176 
177 // Functions that use ASSERT_* must return void, so we use an out parameter
178 // here instead of returning.
ExpectSuccessOnParse(absl::string_view raw_json,Answer * out=nullptr)179 void ExpectSuccessOnParse(absl::string_view raw_json, Answer* out = nullptr) {
180   ErrorOr<Json::Value> root = json::Parse(raw_json);
181   // Must be a valid JSON object, but not a valid answer.
182   ASSERT_TRUE(root.is_value());
183 
184   Answer answer;
185   ASSERT_TRUE(Answer::ParseAndValidate(std::move(root.value()), &answer));
186   EXPECT_TRUE(answer.IsValid());
187   if (out) {
188     *out = std::move(answer);
189   }
190 }
191 
192 }  // anonymous namespace
193 
TEST(AnswerMessagesTest,ProperlyPopulatedAnswerSerializesProperly)194 TEST(AnswerMessagesTest, ProperlyPopulatedAnswerSerializesProperly) {
195   ASSERT_TRUE(kValidAnswer.IsValid());
196   Json::Value root = kValidAnswer.ToJson();
197   EXPECT_EQ(root["udpPort"], 1234);
198 
199   Json::Value sendIndexes = std::move(root["sendIndexes"]);
200   EXPECT_EQ(sendIndexes.type(), Json::ValueType::arrayValue);
201   EXPECT_EQ(sendIndexes[0], 1);
202   EXPECT_EQ(sendIndexes[1], 2);
203   EXPECT_EQ(sendIndexes[2], 3);
204 
205   Json::Value ssrcs = std::move(root["ssrcs"]);
206   EXPECT_EQ(ssrcs.type(), Json::ValueType::arrayValue);
207   EXPECT_EQ(ssrcs[0], 123u);
208   EXPECT_EQ(ssrcs[1], 456u);
209 
210   Json::Value constraints = std::move(root["constraints"]);
211   Json::Value audio = std::move(constraints["audio"]);
212   EXPECT_EQ(audio.type(), Json::ValueType::objectValue);
213   EXPECT_EQ(audio["maxSampleRate"], 96000);
214   EXPECT_EQ(audio["maxChannels"], 7);
215   EXPECT_EQ(audio["minBitRate"], 32000);
216   EXPECT_EQ(audio["maxBitRate"], 96000);
217   EXPECT_EQ(audio["maxDelay"], 2000);
218 
219   Json::Value video = std::move(constraints["video"]);
220   EXPECT_EQ(video.type(), Json::ValueType::objectValue);
221   EXPECT_EQ(video["maxPixelsPerSecond"], 40000.0);
222   EXPECT_EQ(video["minBitRate"], 300000);
223   EXPECT_EQ(video["maxBitRate"], 144000000);
224   EXPECT_EQ(video["maxDelay"], 3000);
225 
226   Json::Value min_dimensions = std::move(video["minDimensions"]);
227   EXPECT_EQ(min_dimensions.type(), Json::ValueType::objectValue);
228   EXPECT_EQ(min_dimensions["width"], 320);
229   EXPECT_EQ(min_dimensions["height"], 480);
230   EXPECT_EQ(min_dimensions["frameRate"], "15000/101");
231 
232   Json::Value max_dimensions = std::move(video["maxDimensions"]);
233   EXPECT_EQ(max_dimensions.type(), Json::ValueType::objectValue);
234   EXPECT_EQ(max_dimensions["width"], 1920);
235   EXPECT_EQ(max_dimensions["height"], 1080);
236   EXPECT_EQ(max_dimensions["frameRate"], "288/2");
237 
238   Json::Value display = std::move(root["display"]);
239   EXPECT_EQ(display.type(), Json::ValueType::objectValue);
240   EXPECT_EQ(display["aspectRatio"], "16:9");
241   EXPECT_EQ(display["scaling"], "sender");
242 
243   Json::Value dimensions = std::move(display["dimensions"]);
244   EXPECT_EQ(dimensions.type(), Json::ValueType::objectValue);
245   EXPECT_EQ(dimensions["width"], 640);
246   EXPECT_EQ(dimensions["height"], 480);
247   EXPECT_EQ(dimensions["frameRate"], "30");
248 
249   Json::Value receiver_rtcp_event_log = std::move(root["receiverRtcpEventLog"]);
250   EXPECT_EQ(receiver_rtcp_event_log.type(), Json::ValueType::arrayValue);
251   EXPECT_EQ(receiver_rtcp_event_log[0], 7);
252   EXPECT_EQ(receiver_rtcp_event_log[1], 8);
253   EXPECT_EQ(receiver_rtcp_event_log[2], 9);
254 
255   Json::Value receiver_rtcp_dscp = std::move(root["receiverRtcpDscp"]);
256   EXPECT_EQ(receiver_rtcp_dscp.type(), Json::ValueType::arrayValue);
257   EXPECT_EQ(receiver_rtcp_dscp[0], 11);
258   EXPECT_EQ(receiver_rtcp_dscp[1], 12);
259   EXPECT_EQ(receiver_rtcp_dscp[2], 13);
260 
261   EXPECT_EQ(root["receiverGetStatus"], true);
262 
263   Json::Value rtp_extensions = std::move(root["rtpExtensions"]);
264   EXPECT_EQ(rtp_extensions.type(), Json::ValueType::arrayValue);
265   EXPECT_EQ(rtp_extensions[0], "foo");
266   EXPECT_EQ(rtp_extensions[1], "bar");
267 }
268 
TEST(AnswerMessagesTest,EmptyArraysOmitted)269 TEST(AnswerMessagesTest, EmptyArraysOmitted) {
270   Answer missing_event_log = kValidAnswer;
271   missing_event_log.receiver_rtcp_event_log.clear();
272   ASSERT_TRUE(missing_event_log.IsValid());
273   Json::Value root = missing_event_log.ToJson();
274   EXPECT_FALSE(root["receiverRtcpEventLog"]);
275 
276   Answer missing_rtcp_dscp = kValidAnswer;
277   missing_rtcp_dscp.receiver_rtcp_dscp.clear();
278   ASSERT_TRUE(missing_rtcp_dscp.IsValid());
279   root = missing_rtcp_dscp.ToJson();
280   EXPECT_FALSE(root["receiverRtcpDscp"]);
281 
282   Answer missing_extensions = kValidAnswer;
283   missing_extensions.rtp_extensions.clear();
284   ASSERT_TRUE(missing_extensions.IsValid());
285   root = missing_extensions.ToJson();
286   EXPECT_FALSE(root["rtpExtensions"]);
287 }
288 
TEST(AnswerMessagesTest,InvalidDimensionsCauseInvalid)289 TEST(AnswerMessagesTest, InvalidDimensionsCauseInvalid) {
290   Answer invalid_dimensions = kValidAnswer;
291   invalid_dimensions.display->dimensions->width = -1;
292   EXPECT_FALSE(invalid_dimensions.IsValid());
293 }
294 
TEST(AnswerMessagesTest,InvalidAudioConstraintsCauseError)295 TEST(AnswerMessagesTest, InvalidAudioConstraintsCauseError) {
296   Answer invalid_audio = kValidAnswer;
297   invalid_audio.constraints->audio.max_bit_rate =
298       invalid_audio.constraints->audio.min_bit_rate - 1;
299   EXPECT_FALSE(invalid_audio.IsValid());
300 }
301 
TEST(AnswerMessagesTest,InvalidVideoConstraintsCauseError)302 TEST(AnswerMessagesTest, InvalidVideoConstraintsCauseError) {
303   Answer invalid_video = kValidAnswer;
304   invalid_video.constraints->video.max_pixels_per_second = -1.0;
305   EXPECT_FALSE(invalid_video.IsValid());
306 }
307 
TEST(AnswerMessagesTest,InvalidDisplayDescriptionsCauseError)308 TEST(AnswerMessagesTest, InvalidDisplayDescriptionsCauseError) {
309   Answer invalid_display = kValidAnswer;
310   invalid_display.display->aspect_ratio = {0, 0};
311   EXPECT_FALSE(invalid_display.IsValid());
312 }
313 
TEST(AnswerMessagesTest,InvalidUdpPortsCauseError)314 TEST(AnswerMessagesTest, InvalidUdpPortsCauseError) {
315   Answer invalid_port = kValidAnswer;
316   invalid_port.udp_port = 65536;
317   EXPECT_FALSE(invalid_port.IsValid());
318 }
319 
TEST(AnswerMessagesTest,CanParseValidAnswerJson)320 TEST(AnswerMessagesTest, CanParseValidAnswerJson) {
321   Answer answer;
322   ExpectSuccessOnParse(kValidAnswerJson, &answer);
323   ExpectEqualsValidAnswerJson(answer);
324 }
325 
326 // In practice, the rtpExtensions, receiverRtcpDscp, and receiverRtcpEventLog
327 // fields may be missing from some receivers. We handle this case by treating
328 // them as empty.
TEST(AnswerMessagesTest,SucceedsWithMissingRtpFields)329 TEST(AnswerMessagesTest, SucceedsWithMissingRtpFields) {
330   ExpectSuccessOnParse(R"({
331   "udpPort": 1234,
332   "sendIndexes": [1, 3],
333   "ssrcs": [1233324, 2234222],
334   "receiverGetStatus": true
335   })");
336 }
337 
TEST(AnswerMessagesTest,ErrorOnEmptyAnswer)338 TEST(AnswerMessagesTest, ErrorOnEmptyAnswer) {
339   ExpectFailureOnParse("{}");
340 }
341 
TEST(AnswerMessagesTest,ErrorOnMissingUdpPort)342 TEST(AnswerMessagesTest, ErrorOnMissingUdpPort) {
343   ExpectFailureOnParse(R"({
344     "sendIndexes": [1, 3],
345     "ssrcs": [1233324, 2234222],
346     "receiverGetStatus": true
347   })");
348 }
349 
TEST(AnswerMessagesTest,ErrorOnMissingSsrcs)350 TEST(AnswerMessagesTest, ErrorOnMissingSsrcs) {
351   ExpectFailureOnParse(R"({
352     "udpPort": 1234,
353     "sendIndexes": [1, 3],
354     "receiverGetStatus": true
355   })");
356 }
357 
TEST(AnswerMessagesTest,ErrorOnMissingSendIndexes)358 TEST(AnswerMessagesTest, ErrorOnMissingSendIndexes) {
359   ExpectFailureOnParse(R"({
360     "udpPort": 1234,
361     "ssrcs": [1233324, 2234222],
362     "receiverGetStatus": true
363   })");
364 }
365 
TEST(AnswerMessagesTest,AssumesNoReportingIfGetStatusFalse)366 TEST(AnswerMessagesTest, AssumesNoReportingIfGetStatusFalse) {
367   Answer answer;
368   ExpectSuccessOnParse(R"({
369     "udpPort": 1234,
370     "sendIndexes": [1, 3],
371     "ssrcs": [1233324, 2234222]
372   })",
373                        &answer);
374 
375   EXPECT_FALSE(answer.supports_wifi_status_reporting);
376 }
377 
TEST(AnswerMessagesTest,AllowsReceiverSideScaling)378 TEST(AnswerMessagesTest, AllowsReceiverSideScaling) {
379   Answer answer;
380   ExpectSuccessOnParse(R"({
381   "udpPort": 1234,
382   "sendIndexes": [1, 3],
383   "ssrcs": [1233324, 2234222],
384   "display": {
385     "dimensions": {
386       "width": 1920,
387       "height": 1080,
388       "frameRate": "60000/1001"
389     },
390     "aspectRatio": "64:27",
391     "scaling": "receiver"
392     }
393   })",
394                        &answer);
395   ASSERT_TRUE(answer.display.has_value());
396   EXPECT_EQ(answer.display->aspect_ratio_constraint.value(),
397             AspectRatioConstraint::kVariable);
398 }
399 
TEST(AnswerMessagesTest,AssumesMinBitRateIfOmitted)400 TEST(AnswerMessagesTest, AssumesMinBitRateIfOmitted) {
401   Answer answer;
402   ExpectSuccessOnParse(R"({
403     "udpPort": 1234,
404     "sendIndexes": [1, 3],
405     "ssrcs": [1233324, 2234222],
406     "constraints": {
407       "audio": {
408         "maxSampleRate": 96000,
409         "maxChannels": 5,
410         "maxBitRate": 320000,
411         "maxDelay": 5000
412       },
413       "video": {
414         "maxPixelsPerSecond": 62208000,
415         "maxDimensions": {
416           "width": 1920,
417           "height": 1080,
418           "frameRate": "60"
419         },
420         "maxBitRate": 10000000,
421         "maxDelay": 5000
422       }
423     },
424     "receiverGetStatus": true
425   })",
426                        &answer);
427 
428   EXPECT_EQ(32000, answer.constraints->audio.min_bit_rate);
429   EXPECT_EQ(300000, answer.constraints->video.min_bit_rate);
430 }
431 
432 // Instead of testing all possible json parsing options for validity, we
433 // can instead directly test the IsValid() methods.
TEST(AnswerMessagesTest,AudioConstraintsIsValid)434 TEST(AnswerMessagesTest, AudioConstraintsIsValid) {
435   constexpr AudioConstraints kInvalidSampleRate{0, 456, 300, 9920,
436                                                 milliseconds(123)};
437   constexpr AudioConstraints kInvalidMaxChannels{123, 0, 300, 9920,
438                                                  milliseconds(123)};
439   constexpr AudioConstraints kInvalidMinBitRate{123, 456, 0, 9920,
440                                                 milliseconds(123)};
441   constexpr AudioConstraints kInvalidMaxBitRate{123, 456, 300, 0,
442                                                 milliseconds(123)};
443   constexpr AudioConstraints kInvalidMaxDelay{123, 456, 300, 0,
444                                               milliseconds(0)};
445 
446   EXPECT_TRUE(kValidAudioConstraints.IsValid());
447   EXPECT_FALSE(kInvalidSampleRate.IsValid());
448   EXPECT_FALSE(kInvalidMaxChannels.IsValid());
449   EXPECT_FALSE(kInvalidMinBitRate.IsValid());
450   EXPECT_FALSE(kInvalidMaxBitRate.IsValid());
451   EXPECT_FALSE(kInvalidMaxDelay.IsValid());
452 }
453 
TEST(AnswerMessagesTest,DimensionsIsValid)454 TEST(AnswerMessagesTest, DimensionsIsValid) {
455   // NOTE: in some cases (such as min dimensions) a frame rate of zero is valid.
456   constexpr Dimensions kValidZeroFrameRate{1920, 1080, SimpleFraction{0, 60}};
457   constexpr Dimensions kInvalidWidth{0, 1080, SimpleFraction{60, 1}};
458   constexpr Dimensions kInvalidHeight{1920, 0, SimpleFraction{60, 1}};
459   constexpr Dimensions kInvalidFrameRateZeroDenominator{1920, 1080,
460                                                         SimpleFraction{60, 0}};
461   constexpr Dimensions kInvalidFrameRateNegativeNumerator{
462       1920, 1080, SimpleFraction{-1, 30}};
463   constexpr Dimensions kInvalidFrameRateNegativeDenominator{
464       1920, 1080, SimpleFraction{30, -1}};
465 
466   EXPECT_TRUE(kValidDimensions.IsValid());
467   EXPECT_TRUE(kValidZeroFrameRate.IsValid());
468   EXPECT_FALSE(kInvalidWidth.IsValid());
469   EXPECT_FALSE(kInvalidHeight.IsValid());
470   EXPECT_FALSE(kInvalidFrameRateZeroDenominator.IsValid());
471   EXPECT_FALSE(kInvalidFrameRateNegativeNumerator.IsValid());
472   EXPECT_FALSE(kInvalidFrameRateNegativeDenominator.IsValid());
473 }
474 
TEST(AnswerMessagesTest,VideoConstraintsIsValid)475 TEST(AnswerMessagesTest, VideoConstraintsIsValid) {
476   VideoConstraints invalid_max_pixels_per_second = kValidVideoConstraints;
477   invalid_max_pixels_per_second.max_pixels_per_second = 0;
478 
479   VideoConstraints invalid_min_dimensions = kValidVideoConstraints;
480   invalid_min_dimensions.min_dimensions->width = 0;
481 
482   VideoConstraints invalid_max_dimensions = kValidVideoConstraints;
483   invalid_max_dimensions.max_dimensions.height = 0;
484 
485   VideoConstraints invalid_min_bit_rate = kValidVideoConstraints;
486   invalid_min_bit_rate.min_bit_rate = 0;
487 
488   VideoConstraints invalid_max_bit_rate = kValidVideoConstraints;
489   invalid_max_bit_rate.max_bit_rate = invalid_max_bit_rate.min_bit_rate - 1;
490 
491   VideoConstraints invalid_max_delay = kValidVideoConstraints;
492   invalid_max_delay.max_delay = milliseconds(0);
493 
494   EXPECT_TRUE(kValidVideoConstraints.IsValid());
495   EXPECT_FALSE(invalid_max_pixels_per_second.IsValid());
496   EXPECT_FALSE(invalid_min_dimensions.IsValid());
497   EXPECT_FALSE(invalid_max_dimensions.IsValid());
498   EXPECT_FALSE(invalid_min_bit_rate.IsValid());
499   EXPECT_FALSE(invalid_max_bit_rate.IsValid());
500   EXPECT_FALSE(invalid_max_delay.IsValid());
501 }
502 
TEST(AnswerMessagesTest,ConstraintsIsValid)503 TEST(AnswerMessagesTest, ConstraintsIsValid) {
504   VideoConstraints invalid_video_constraints = kValidVideoConstraints;
505   invalid_video_constraints.max_pixels_per_second = 0;
506 
507   AudioConstraints invalid_audio_constraints = kValidAudioConstraints;
508   invalid_audio_constraints.max_bit_rate = 0;
509 
510   const Constraints valid{kValidAudioConstraints, kValidVideoConstraints};
511   const Constraints invalid_audio{kValidAudioConstraints,
512                                   invalid_video_constraints};
513   const Constraints invalid_video{invalid_audio_constraints,
514                                   kValidVideoConstraints};
515 
516   EXPECT_TRUE(valid.IsValid());
517   EXPECT_FALSE(invalid_audio.IsValid());
518   EXPECT_FALSE(invalid_video.IsValid());
519 }
520 
TEST(AnswerMessagesTest,AspectRatioIsValid)521 TEST(AnswerMessagesTest, AspectRatioIsValid) {
522   constexpr AspectRatio kValid{16, 9};
523   constexpr AspectRatio kInvalidWidth{0, 9};
524   constexpr AspectRatio kInvalidHeight{16, 0};
525 
526   EXPECT_TRUE(kValid.IsValid());
527   EXPECT_FALSE(kInvalidWidth.IsValid());
528   EXPECT_FALSE(kInvalidHeight.IsValid());
529 }
530 
TEST(AnswerMessagesTest,AspectRatioParseAndValidate)531 TEST(AnswerMessagesTest, AspectRatioParseAndValidate) {
532   const Json::Value kValid = "16:9";
533   const Json::Value kWrongDelimiter = "16-9";
534   const Json::Value kTooManyFields = "16:9:3";
535   const Json::Value kTooFewFields = "1:";
536   const Json::Value kNoDelimiter = "12345";
537   const Json::Value kNegativeWidth = "-123:2345";
538   const Json::Value kNegativeHeight = "22:-7";
539   const Json::Value kNegativeBoth = "22:-7";
540   const Json::Value kNonNumberWidth = "twenty2#:9";
541   const Json::Value kNonNumberHeight = "2:thirty";
542   const Json::Value kZeroWidth = "0:9";
543   const Json::Value kZeroHeight = "16:0";
544 
545   AspectRatio out;
546   EXPECT_TRUE(AspectRatio::ParseAndValidate(kValid, &out));
547   EXPECT_EQ(out.width, 16);
548   EXPECT_EQ(out.height, 9);
549   EXPECT_FALSE(AspectRatio::ParseAndValidate(kWrongDelimiter, &out));
550   EXPECT_FALSE(AspectRatio::ParseAndValidate(kTooManyFields, &out));
551   EXPECT_FALSE(AspectRatio::ParseAndValidate(kTooFewFields, &out));
552   EXPECT_FALSE(AspectRatio::ParseAndValidate(kWrongDelimiter, &out));
553   EXPECT_FALSE(AspectRatio::ParseAndValidate(kNoDelimiter, &out));
554   EXPECT_FALSE(AspectRatio::ParseAndValidate(kNegativeWidth, &out));
555   EXPECT_FALSE(AspectRatio::ParseAndValidate(kNegativeHeight, &out));
556   EXPECT_FALSE(AspectRatio::ParseAndValidate(kNegativeBoth, &out));
557   EXPECT_FALSE(AspectRatio::ParseAndValidate(kNonNumberWidth, &out));
558   EXPECT_FALSE(AspectRatio::ParseAndValidate(kNonNumberHeight, &out));
559   EXPECT_FALSE(AspectRatio::ParseAndValidate(kZeroWidth, &out));
560   EXPECT_FALSE(AspectRatio::ParseAndValidate(kZeroHeight, &out));
561 }
562 
TEST(AnswerMessagesTest,DisplayDescriptionParseAndValidate)563 TEST(AnswerMessagesTest, DisplayDescriptionParseAndValidate) {
564   Json::Value valid_scaling;
565   valid_scaling["scaling"] = "receiver";
566   Json::Value invalid_scaling;
567   invalid_scaling["scaling"] = "embedder";
568   Json::Value invalid_scaling_valid_ratio;
569   invalid_scaling_valid_ratio["scaling"] = "embedder";
570   invalid_scaling_valid_ratio["aspectRatio"] = "16:9";
571 
572   Json::Value dimensions;
573   dimensions["width"] = 1920;
574   dimensions["height"] = 1080;
575   dimensions["frameRate"] = "30";
576   Json::Value valid_dimensions;
577   valid_dimensions["dimensions"] = dimensions;
578 
579   Json::Value dimensions_invalid = dimensions;
580   dimensions_invalid["frameRate"] = "infinity";
581   Json::Value invalid_dimensions;
582   invalid_dimensions["dimensions"] = dimensions_invalid;
583 
584   Json::Value aspect_ratio_and_constraint;
585   aspect_ratio_and_constraint["scaling"] = "sender";
586   aspect_ratio_and_constraint["aspectRatio"] = "4:3";
587 
588   DisplayDescription out;
589   ASSERT_TRUE(DisplayDescription::ParseAndValidate(valid_scaling, &out));
590   ASSERT_TRUE(out.aspect_ratio_constraint.has_value());
591   EXPECT_EQ(out.aspect_ratio_constraint.value(),
592             AspectRatioConstraint::kVariable);
593 
594   EXPECT_FALSE(DisplayDescription::ParseAndValidate(invalid_scaling, &out));
595   EXPECT_TRUE(
596       DisplayDescription::ParseAndValidate(invalid_scaling_valid_ratio, &out));
597 
598   ASSERT_TRUE(DisplayDescription::ParseAndValidate(valid_dimensions, &out));
599   ASSERT_TRUE(out.dimensions.has_value());
600   EXPECT_EQ(1920, out.dimensions->width);
601   EXPECT_EQ(1080, out.dimensions->height);
602   EXPECT_EQ((SimpleFraction{30, 1}), out.dimensions->frame_rate);
603 
604   EXPECT_FALSE(DisplayDescription::ParseAndValidate(invalid_dimensions, &out));
605 
606   ASSERT_TRUE(
607       DisplayDescription::ParseAndValidate(aspect_ratio_and_constraint, &out));
608   EXPECT_EQ(AspectRatioConstraint::kFixed, out.aspect_ratio_constraint.value());
609 }
610 
TEST(AnswerMessagesTest,DisplayDescriptionIsValid)611 TEST(AnswerMessagesTest, DisplayDescriptionIsValid) {
612   const DisplayDescription kInvalidEmptyDescription{
613       absl::optional<Dimensions>{}, absl::optional<AspectRatio>{},
614       absl::optional<AspectRatioConstraint>{}};
615 
616   DisplayDescription has_valid_dimensions = kInvalidEmptyDescription;
617   has_valid_dimensions.dimensions =
618       absl::optional<Dimensions>(kValidDimensions);
619 
620   DisplayDescription has_invalid_dimensions = kInvalidEmptyDescription;
621   has_invalid_dimensions.dimensions =
622       absl::optional<Dimensions>(kValidDimensions);
623   has_invalid_dimensions.dimensions->width = 0;
624 
625   DisplayDescription has_aspect_ratio = kInvalidEmptyDescription;
626   has_aspect_ratio.aspect_ratio =
627       absl::optional<AspectRatio>{AspectRatio{16, 9}};
628 
629   DisplayDescription has_invalid_aspect_ratio = kInvalidEmptyDescription;
630   has_invalid_aspect_ratio.aspect_ratio =
631       absl::optional<AspectRatio>{AspectRatio{0, 20}};
632 
633   DisplayDescription has_aspect_ratio_constraint = kInvalidEmptyDescription;
634   has_aspect_ratio_constraint.aspect_ratio_constraint =
635       absl::optional<AspectRatioConstraint>(AspectRatioConstraint::kFixed);
636 
637   DisplayDescription has_constraint_and_dimensions =
638       has_aspect_ratio_constraint;
639   has_constraint_and_dimensions.dimensions =
640       absl::optional<Dimensions>(kValidDimensions);
641 
642   DisplayDescription has_constraint_and_ratio = has_aspect_ratio_constraint;
643   has_constraint_and_ratio.aspect_ratio = AspectRatio{4, 3};
644 
645   EXPECT_FALSE(kInvalidEmptyDescription.IsValid());
646   EXPECT_TRUE(has_valid_dimensions.IsValid());
647   EXPECT_FALSE(has_invalid_dimensions.IsValid());
648   EXPECT_TRUE(has_aspect_ratio.IsValid());
649   EXPECT_FALSE(has_invalid_aspect_ratio.IsValid());
650   EXPECT_FALSE(has_aspect_ratio_constraint.IsValid());
651   EXPECT_TRUE(has_constraint_and_dimensions.IsValid());
652 }
653 
654 // Instead of being tested here, Answer's IsValid is checked in all other
655 // relevant tests.
656 
657 }  // namespace cast
658 }  // namespace openscreen
659