Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(51)

Side by Side Diff: webrtc/api/rtpsenderreceiver_unittest.cc

Issue 1766653002: Replace SetCapturer and SetCaptureDevice by SetSource. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Make SetSource tolerate unknown ssrc and source == NULL. Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright 2012 The WebRTC project authors. All Rights Reserved. 2 * Copyright 2012 The WebRTC project authors. All Rights Reserved.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license 4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source 5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found 6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may 7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree. 8 * be found in the AUTHORS file in the root of the source tree.
9 */ 9 */
10 10
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
59 } 59 }
60 60
61 private: 61 private:
62 rtc::scoped_ptr<AudioSinkInterface> sink_; 62 rtc::scoped_ptr<AudioSinkInterface> sink_;
63 }; 63 };
64 64
65 // Helper class to test RtpSender/RtpReceiver. 65 // Helper class to test RtpSender/RtpReceiver.
66 class MockVideoProvider : public VideoProviderInterface { 66 class MockVideoProvider : public VideoProviderInterface {
67 public: 67 public:
68 virtual ~MockVideoProvider() {} 68 virtual ~MockVideoProvider() {}
69 MOCK_METHOD2(SetCaptureDevice, 69 MOCK_METHOD2(SetSource,
70 bool(uint32_t ssrc, cricket::VideoCapturer* camera)); 70 bool(uint32_t ssrc,
71 rtc::VideoSourceInterface<cricket::VideoFrame>* source));
71 MOCK_METHOD3(SetVideoPlayout, 72 MOCK_METHOD3(SetVideoPlayout,
72 void(uint32_t ssrc, 73 void(uint32_t ssrc,
73 bool enable, 74 bool enable,
74 rtc::VideoSinkInterface<cricket::VideoFrame>* sink)); 75 rtc::VideoSinkInterface<cricket::VideoFrame>* sink));
75 MOCK_METHOD3(SetVideoSend, 76 MOCK_METHOD3(SetVideoSend,
76 void(uint32_t ssrc, 77 void(uint32_t ssrc,
77 bool enable, 78 bool enable,
78 const cricket::VideoOptions* options)); 79 const cricket::VideoOptions* options));
79 }; 80 };
80 81
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
125 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, true, _, _)); 126 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, true, _, _));
126 audio_rtp_sender_ = 127 audio_rtp_sender_ =
127 new AudioRtpSender(stream_->GetAudioTracks()[0], stream_->label(), 128 new AudioRtpSender(stream_->GetAudioTracks()[0], stream_->label(),
128 &audio_provider_, nullptr); 129 &audio_provider_, nullptr);
129 audio_rtp_sender_->SetSsrc(kAudioSsrc); 130 audio_rtp_sender_->SetSsrc(kAudioSsrc);
130 } 131 }
131 132
132 void CreateVideoRtpSender() { 133 void CreateVideoRtpSender() {
133 AddVideoTrack(false); 134 AddVideoTrack(false);
134 EXPECT_CALL(video_provider_, 135 EXPECT_CALL(video_provider_,
135 SetCaptureDevice( 136 SetSource(
136 kVideoSsrc, video_track_->GetSource()->GetVideoCapturer())); 137 kVideoSsrc, video_track_->GetSource()->GetVideoCapturer()));
137 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, true, _)); 138 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, true, _));
138 video_rtp_sender_ = new VideoRtpSender(stream_->GetVideoTracks()[0], 139 video_rtp_sender_ = new VideoRtpSender(stream_->GetVideoTracks()[0],
139 stream_->label(), &video_provider_); 140 stream_->label(), &video_provider_);
140 video_rtp_sender_->SetSsrc(kVideoSsrc); 141 video_rtp_sender_->SetSsrc(kVideoSsrc);
141 } 142 }
142 143
143 void DestroyAudioRtpSender() { 144 void DestroyAudioRtpSender() {
144 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, false, _, _)) 145 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, false, _, _))
145 .Times(1); 146 .Times(1);
146 audio_rtp_sender_ = nullptr; 147 audio_rtp_sender_ = nullptr;
147 } 148 }
148 149
149 void DestroyVideoRtpSender() { 150 void DestroyVideoRtpSender() {
150 EXPECT_CALL(video_provider_, SetCaptureDevice(kVideoSsrc, NULL)).Times(1); 151 EXPECT_CALL(video_provider_, SetSource(kVideoSsrc, NULL)).Times(1);
151 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, false, _)).Times(1); 152 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, false, _)).Times(1);
152 video_rtp_sender_ = nullptr; 153 video_rtp_sender_ = nullptr;
153 } 154 }
154 155
155 void CreateAudioRtpReceiver() { 156 void CreateAudioRtpReceiver() {
156 audio_track_ = AudioTrack::Create( 157 audio_track_ = AudioTrack::Create(
157 kAudioTrackId, RemoteAudioSource::Create(kAudioSsrc, NULL)); 158 kAudioTrackId, RemoteAudioSource::Create(kAudioSsrc, NULL));
158 EXPECT_TRUE(stream_->AddTrack(audio_track_)); 159 EXPECT_TRUE(stream_->AddTrack(audio_track_));
159 EXPECT_CALL(audio_provider_, SetAudioPlayout(kAudioSsrc, true)); 160 EXPECT_CALL(audio_provider_, SetAudioPlayout(kAudioSsrc, true));
160 audio_rtp_receiver_ = new AudioRtpReceiver(stream_->GetAudioTracks()[0], 161 audio_rtp_receiver_ = new AudioRtpReceiver(stream_->GetAudioTracks()[0],
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after
345 } 346 }
346 347
347 // Test that a video sender calls the expected methods on the provider once 348 // Test that a video sender calls the expected methods on the provider once
348 // it has a track and SSRC, when the SSRC is set first. 349 // it has a track and SSRC, when the SSRC is set first.
349 TEST_F(RtpSenderReceiverTest, VideoSenderEarlyWarmupSsrcThenTrack) { 350 TEST_F(RtpSenderReceiverTest, VideoSenderEarlyWarmupSsrcThenTrack) {
350 AddVideoTrack(false); 351 AddVideoTrack(false);
351 rtc::scoped_refptr<VideoRtpSender> sender = 352 rtc::scoped_refptr<VideoRtpSender> sender =
352 new VideoRtpSender(&video_provider_); 353 new VideoRtpSender(&video_provider_);
353 sender->SetSsrc(kVideoSsrc); 354 sender->SetSsrc(kVideoSsrc);
354 EXPECT_CALL(video_provider_, 355 EXPECT_CALL(video_provider_,
355 SetCaptureDevice(kVideoSsrc, 356 SetSource(kVideoSsrc,
356 video_track_->GetSource()->GetVideoCapturer())); 357 video_track_->GetSource()->GetVideoCapturer()));
357 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, true, _)); 358 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, true, _));
358 sender->SetTrack(video_track_); 359 sender->SetTrack(video_track_);
359 360
360 // Calls expected from destructor. 361 // Calls expected from destructor.
361 EXPECT_CALL(video_provider_, SetCaptureDevice(kVideoSsrc, nullptr)).Times(1); 362 EXPECT_CALL(video_provider_, SetSource(kVideoSsrc, nullptr)).Times(1);
362 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, false, _)).Times(1); 363 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, false, _)).Times(1);
363 } 364 }
364 365
365 // Test that a video sender calls the expected methods on the provider once 366 // Test that a video sender calls the expected methods on the provider once
366 // it has a track and SSRC, when the SSRC is set last. 367 // it has a track and SSRC, when the SSRC is set last.
367 TEST_F(RtpSenderReceiverTest, VideoSenderEarlyWarmupTrackThenSsrc) { 368 TEST_F(RtpSenderReceiverTest, VideoSenderEarlyWarmupTrackThenSsrc) {
368 AddVideoTrack(false); 369 AddVideoTrack(false);
369 rtc::scoped_refptr<VideoRtpSender> sender = 370 rtc::scoped_refptr<VideoRtpSender> sender =
370 new VideoRtpSender(&video_provider_); 371 new VideoRtpSender(&video_provider_);
371 sender->SetTrack(video_track_); 372 sender->SetTrack(video_track_);
372 EXPECT_CALL(video_provider_, 373 EXPECT_CALL(video_provider_,
373 SetCaptureDevice(kVideoSsrc, 374 SetSource(kVideoSsrc,
374 video_track_->GetSource()->GetVideoCapturer())); 375 video_track_->GetSource()->GetVideoCapturer()));
375 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, true, _)); 376 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, true, _));
376 sender->SetSsrc(kVideoSsrc); 377 sender->SetSsrc(kVideoSsrc);
377 378
378 // Calls expected from destructor. 379 // Calls expected from destructor.
379 EXPECT_CALL(video_provider_, SetCaptureDevice(kVideoSsrc, nullptr)).Times(1); 380 EXPECT_CALL(video_provider_, SetSource(kVideoSsrc, nullptr)).Times(1);
380 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, false, _)).Times(1); 381 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, false, _)).Times(1);
381 } 382 }
382 383
383 // Test that the sender is disconnected from the provider when its SSRC is 384 // Test that the sender is disconnected from the provider when its SSRC is
384 // set to 0. 385 // set to 0.
385 TEST_F(RtpSenderReceiverTest, AudioSenderSsrcSetToZero) { 386 TEST_F(RtpSenderReceiverTest, AudioSenderSsrcSetToZero) {
386 rtc::scoped_refptr<AudioTrackInterface> track = 387 rtc::scoped_refptr<AudioTrackInterface> track =
387 AudioTrack::Create(kAudioTrackId, nullptr); 388 AudioTrack::Create(kAudioTrackId, nullptr);
388 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, true, _, _)); 389 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, true, _, _));
389 rtc::scoped_refptr<AudioRtpSender> sender = 390 rtc::scoped_refptr<AudioRtpSender> sender =
390 new AudioRtpSender(track, kStreamLabel1, &audio_provider_, nullptr); 391 new AudioRtpSender(track, kStreamLabel1, &audio_provider_, nullptr);
391 sender->SetSsrc(kAudioSsrc); 392 sender->SetSsrc(kAudioSsrc);
392 393
393 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, false, _, _)).Times(1); 394 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, false, _, _)).Times(1);
394 sender->SetSsrc(0); 395 sender->SetSsrc(0);
395 396
396 // Make sure it's SetSsrc that called methods on the provider, and not the 397 // Make sure it's SetSsrc that called methods on the provider, and not the
397 // destructor. 398 // destructor.
398 EXPECT_CALL(audio_provider_, SetAudioSend(_, _, _, _)).Times(0); 399 EXPECT_CALL(audio_provider_, SetAudioSend(_, _, _, _)).Times(0);
399 } 400 }
400 401
401 // Test that the sender is disconnected from the provider when its SSRC is 402 // Test that the sender is disconnected from the provider when its SSRC is
402 // set to 0. 403 // set to 0.
403 TEST_F(RtpSenderReceiverTest, VideoSenderSsrcSetToZero) { 404 TEST_F(RtpSenderReceiverTest, VideoSenderSsrcSetToZero) {
404 AddVideoTrack(false); 405 AddVideoTrack(false);
405 EXPECT_CALL(video_provider_, 406 EXPECT_CALL(video_provider_,
406 SetCaptureDevice(kVideoSsrc, 407 SetSource(kVideoSsrc,
407 video_track_->GetSource()->GetVideoCapturer())); 408 video_track_->GetSource()->GetVideoCapturer()));
408 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, true, _)); 409 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, true, _));
409 rtc::scoped_refptr<VideoRtpSender> sender = 410 rtc::scoped_refptr<VideoRtpSender> sender =
410 new VideoRtpSender(video_track_, kStreamLabel1, &video_provider_); 411 new VideoRtpSender(video_track_, kStreamLabel1, &video_provider_);
411 sender->SetSsrc(kVideoSsrc); 412 sender->SetSsrc(kVideoSsrc);
412 413
413 EXPECT_CALL(video_provider_, SetCaptureDevice(kVideoSsrc, nullptr)).Times(1); 414 EXPECT_CALL(video_provider_, SetSource(kVideoSsrc, nullptr)).Times(1);
414 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, false, _)).Times(1); 415 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, false, _)).Times(1);
415 sender->SetSsrc(0); 416 sender->SetSsrc(0);
416 417
417 // Make sure it's SetSsrc that called methods on the provider, and not the 418 // Make sure it's SetSsrc that called methods on the provider, and not the
418 // destructor. 419 // destructor.
419 EXPECT_CALL(video_provider_, SetCaptureDevice(_, _)).Times(0); 420 EXPECT_CALL(video_provider_, SetSource(_, _)).Times(0);
420 EXPECT_CALL(video_provider_, SetVideoSend(_, _, _)).Times(0); 421 EXPECT_CALL(video_provider_, SetVideoSend(_, _, _)).Times(0);
421 } 422 }
422 423
423 TEST_F(RtpSenderReceiverTest, AudioSenderTrackSetToNull) { 424 TEST_F(RtpSenderReceiverTest, AudioSenderTrackSetToNull) {
424 rtc::scoped_refptr<AudioTrackInterface> track = 425 rtc::scoped_refptr<AudioTrackInterface> track =
425 AudioTrack::Create(kAudioTrackId, nullptr); 426 AudioTrack::Create(kAudioTrackId, nullptr);
426 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, true, _, _)); 427 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, true, _, _));
427 rtc::scoped_refptr<AudioRtpSender> sender = 428 rtc::scoped_refptr<AudioRtpSender> sender =
428 new AudioRtpSender(track, kStreamLabel1, &audio_provider_, nullptr); 429 new AudioRtpSender(track, kStreamLabel1, &audio_provider_, nullptr);
429 sender->SetSsrc(kAudioSsrc); 430 sender->SetSsrc(kAudioSsrc);
430 431
431 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, false, _, _)).Times(1); 432 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, false, _, _)).Times(1);
432 EXPECT_TRUE(sender->SetTrack(nullptr)); 433 EXPECT_TRUE(sender->SetTrack(nullptr));
433 434
434 // Make sure it's SetTrack that called methods on the provider, and not the 435 // Make sure it's SetTrack that called methods on the provider, and not the
435 // destructor. 436 // destructor.
436 EXPECT_CALL(audio_provider_, SetAudioSend(_, _, _, _)).Times(0); 437 EXPECT_CALL(audio_provider_, SetAudioSend(_, _, _, _)).Times(0);
437 } 438 }
438 439
439 TEST_F(RtpSenderReceiverTest, VideoSenderTrackSetToNull) { 440 TEST_F(RtpSenderReceiverTest, VideoSenderTrackSetToNull) {
440 AddVideoTrack(false); 441 AddVideoTrack(false);
441 EXPECT_CALL(video_provider_, 442 EXPECT_CALL(video_provider_,
442 SetCaptureDevice(kVideoSsrc, 443 SetSource(kVideoSsrc,
443 video_track_->GetSource()->GetVideoCapturer())); 444 video_track_->GetSource()->GetVideoCapturer()));
444 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, true, _)); 445 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, true, _));
445 rtc::scoped_refptr<VideoRtpSender> sender = 446 rtc::scoped_refptr<VideoRtpSender> sender =
446 new VideoRtpSender(video_track_, kStreamLabel1, &video_provider_); 447 new VideoRtpSender(video_track_, kStreamLabel1, &video_provider_);
447 sender->SetSsrc(kVideoSsrc); 448 sender->SetSsrc(kVideoSsrc);
448 449
449 EXPECT_CALL(video_provider_, SetCaptureDevice(kVideoSsrc, nullptr)).Times(1); 450 EXPECT_CALL(video_provider_, SetSource(kVideoSsrc, nullptr)).Times(1);
450 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, false, _)).Times(1); 451 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, false, _)).Times(1);
451 EXPECT_TRUE(sender->SetTrack(nullptr)); 452 EXPECT_TRUE(sender->SetTrack(nullptr));
452 453
453 // Make sure it's SetTrack that called methods on the provider, and not the 454 // Make sure it's SetTrack that called methods on the provider, and not the
454 // destructor. 455 // destructor.
455 EXPECT_CALL(video_provider_, SetCaptureDevice(_, _)).Times(0); 456 EXPECT_CALL(video_provider_, SetSource(_, _)).Times(0);
456 EXPECT_CALL(video_provider_, SetVideoSend(_, _, _)).Times(0); 457 EXPECT_CALL(video_provider_, SetVideoSend(_, _, _)).Times(0);
457 } 458 }
458 459
459 TEST_F(RtpSenderReceiverTest, AudioSenderSsrcChanged) { 460 TEST_F(RtpSenderReceiverTest, AudioSenderSsrcChanged) {
460 AddVideoTrack(false); 461 AddVideoTrack(false);
461 rtc::scoped_refptr<AudioTrackInterface> track = 462 rtc::scoped_refptr<AudioTrackInterface> track =
462 AudioTrack::Create(kAudioTrackId, nullptr); 463 AudioTrack::Create(kAudioTrackId, nullptr);
463 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, true, _, _)); 464 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, true, _, _));
464 rtc::scoped_refptr<AudioRtpSender> sender = 465 rtc::scoped_refptr<AudioRtpSender> sender =
465 new AudioRtpSender(track, kStreamLabel1, &audio_provider_, nullptr); 466 new AudioRtpSender(track, kStreamLabel1, &audio_provider_, nullptr);
466 sender->SetSsrc(kAudioSsrc); 467 sender->SetSsrc(kAudioSsrc);
467 468
468 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, false, _, _)).Times(1); 469 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc, false, _, _)).Times(1);
469 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc2, true, _, _)).Times(1); 470 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc2, true, _, _)).Times(1);
470 sender->SetSsrc(kAudioSsrc2); 471 sender->SetSsrc(kAudioSsrc2);
471 472
472 // Calls expected from destructor. 473 // Calls expected from destructor.
473 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc2, false, _, _)).Times(1); 474 EXPECT_CALL(audio_provider_, SetAudioSend(kAudioSsrc2, false, _, _)).Times(1);
474 } 475 }
475 476
476 TEST_F(RtpSenderReceiverTest, VideoSenderSsrcChanged) { 477 TEST_F(RtpSenderReceiverTest, VideoSenderSsrcChanged) {
477 AddVideoTrack(false); 478 AddVideoTrack(false);
478 EXPECT_CALL(video_provider_, 479 EXPECT_CALL(video_provider_,
479 SetCaptureDevice(kVideoSsrc, 480 SetSource(kVideoSsrc,
480 video_track_->GetSource()->GetVideoCapturer())); 481 video_track_->GetSource()->GetVideoCapturer()));
481 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, true, _)); 482 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, true, _));
482 rtc::scoped_refptr<VideoRtpSender> sender = 483 rtc::scoped_refptr<VideoRtpSender> sender =
483 new VideoRtpSender(video_track_, kStreamLabel1, &video_provider_); 484 new VideoRtpSender(video_track_, kStreamLabel1, &video_provider_);
484 sender->SetSsrc(kVideoSsrc); 485 sender->SetSsrc(kVideoSsrc);
485 486
486 EXPECT_CALL(video_provider_, SetCaptureDevice(kVideoSsrc, nullptr)).Times(1); 487 EXPECT_CALL(video_provider_, SetSource(kVideoSsrc, nullptr)).Times(1);
487 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, false, _)).Times(1); 488 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc, false, _)).Times(1);
488 EXPECT_CALL(video_provider_, 489 EXPECT_CALL(video_provider_,
489 SetCaptureDevice(kVideoSsrc2, 490 SetSource(kVideoSsrc2,
490 video_track_->GetSource()->GetVideoCapturer())); 491 video_track_->GetSource()->GetVideoCapturer()));
491 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc2, true, _)); 492 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc2, true, _));
492 sender->SetSsrc(kVideoSsrc2); 493 sender->SetSsrc(kVideoSsrc2);
493 494
494 // Calls expected from destructor. 495 // Calls expected from destructor.
495 EXPECT_CALL(video_provider_, SetCaptureDevice(kVideoSsrc2, nullptr)).Times(1); 496 EXPECT_CALL(video_provider_, SetSource(kVideoSsrc2, nullptr)).Times(1);
496 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc2, false, _)).Times(1); 497 EXPECT_CALL(video_provider_, SetVideoSend(kVideoSsrc2, false, _)).Times(1);
497 } 498 }
498 499
499 } // namespace webrtc 500 } // namespace webrtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698