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