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