Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / renderer / media / media_stream_impl_unittest.cc
index 644f2cd..c94a487 100644 (file)
@@ -3,13 +3,17 @@
 // found in the LICENSE file.
 
 #include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
 #include "base/strings/utf_string_conversions.h"
-#include "content/renderer/media/media_stream_extra_data.h"
+#include "content/child/child_process.h"
+#include "content/renderer/media/media_stream.h"
 #include "content/renderer/media/media_stream_impl.h"
-#include "content/renderer/media/mock_media_stream_dependency_factory.h"
+#include "content/renderer/media/media_stream_track.h"
 #include "content/renderer/media/mock_media_stream_dispatcher.h"
-#include "content/renderer/media/video_capture_impl_manager.h"
+#include "content/renderer/media/mock_media_stream_video_source.h"
+#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/public/platform/WebMediaDeviceInfo.h"
 #include "third_party/WebKit/public/platform/WebMediaStream.h"
 #include "third_party/WebKit/public/platform/WebMediaStreamSource.h"
 #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h"
 
 namespace content {
 
+class MockMediaStreamVideoCapturerSource : public MockMediaStreamVideoSource {
+ public:
+  MockMediaStreamVideoCapturerSource(
+      const StreamDeviceInfo& device,
+      const SourceStoppedCallback& stop_callback,
+      PeerConnectionDependencyFactory* factory)
+  : MockMediaStreamVideoSource(false) {
+    SetDeviceInfo(device);
+    SetStopCallback(stop_callback);
+  }
+};
+
 class MediaStreamImplUnderTest : public MediaStreamImpl {
  public:
   enum RequestState {
@@ -27,10 +43,16 @@ class MediaStreamImplUnderTest : public MediaStreamImpl {
     REQUEST_FAILED,
   };
 
-  MediaStreamImplUnderTest(MediaStreamDispatcher* media_stream_dispatcher,
-                           MediaStreamDependencyFactory* dependency_factory)
-      : MediaStreamImpl(NULL, media_stream_dispatcher, dependency_factory),
-        state_(REQUEST_NOT_STARTED) {
+  MediaStreamImplUnderTest(
+      PeerConnectionDependencyFactory* dependency_factory,
+      scoped_ptr<MediaStreamDispatcher> media_stream_dispatcher)
+      : MediaStreamImpl(
+            NULL, dependency_factory, media_stream_dispatcher.Pass()),
+        state_(REQUEST_NOT_STARTED),
+        result_(NUM_MEDIA_REQUEST_RESULTS),
+        result_name_(""),
+        factory_(dependency_factory),
+        video_source_(NULL) {
   }
 
   void RequestUserMedia() {
@@ -39,102 +61,163 @@ class MediaStreamImplUnderTest : public MediaStreamImpl {
     requestUserMedia(user_media_request);
   }
 
-  virtual void CompleteGetUserMediaRequest(
+  void RequestMediaDevices() {
+    blink::WebMediaDevicesRequest media_devices_request;
+    state_ = REQUEST_NOT_COMPLETE;
+    requestMediaDevices(media_devices_request);
+  }
+
+  virtual void GetUserMediaRequestSucceeded(
       const blink::WebMediaStream& stream,
-      blink::WebUserMediaRequest* request_info,
-      bool request_succeeded) OVERRIDE {
+      blink::WebUserMediaRequest* request_info) OVERRIDE {
     last_generated_stream_ = stream;
-    state_ = request_succeeded ? REQUEST_SUCCEEDED : REQUEST_FAILED;
+    state_ = REQUEST_SUCCEEDED;
   }
 
-  virtual blink::WebMediaStream GetMediaStream(
-      const GURL& url) OVERRIDE {
-    return last_generated_stream_;
+  virtual void GetUserMediaRequestFailed(
+      blink::WebUserMediaRequest* request_info,
+      content::MediaStreamRequestResult result) OVERRIDE {
+    last_generated_stream_.reset();
+    state_ = REQUEST_FAILED;
+    result_ = result;
   }
 
-  using MediaStreamImpl::OnLocalMediaStreamStop;
-  using MediaStreamImpl::OnLocalSourceStop;
+  virtual void GetUserMediaRequestTrackStartedFailed(
+      blink::WebUserMediaRequest* request_info,
+      MediaStreamRequestResult result,
+      const blink::WebString& result_name) OVERRIDE {
+    last_generated_stream_.reset();
+    state_ = REQUEST_FAILED;
+    result_ = result;
+    result_name_ = result_name;
+  }
+
+  virtual void EnumerateDevicesSucceded(
+      blink::WebMediaDevicesRequest* request,
+      blink::WebVector<blink::WebMediaDeviceInfo>& devices) OVERRIDE {
+    state_ = REQUEST_SUCCEEDED;
+    last_devices_ = devices;
+  }
+
+  virtual MediaStreamVideoSource* CreateVideoSource(
+      const StreamDeviceInfo& device,
+      const MediaStreamSource::SourceStoppedCallback& stop_callback) OVERRIDE {
+    video_source_ = new MockMediaStreamVideoCapturerSource(device,
+                                                           stop_callback,
+                                                           factory_);
+    return video_source_;
+  }
 
   const blink::WebMediaStream& last_generated_stream() {
     return last_generated_stream_;
   }
 
+  const blink::WebVector<blink::WebMediaDeviceInfo>& last_devices() {
+    return last_devices_;
+  }
+
+  void ClearLastGeneratedStream() {
+    last_generated_stream_.reset();
+  }
+
+  MockMediaStreamVideoCapturerSource* last_created_video_source() const {
+    return video_source_;
+  }
+
   RequestState request_state() const { return state_; }
+  content::MediaStreamRequestResult error_reason() const { return result_; }
+  blink::WebString error_name() const { return result_name_; }
 
  private:
   blink::WebMediaStream last_generated_stream_;
   RequestState state_;
+  content::MediaStreamRequestResult result_;
+  blink::WebString result_name_;
+  blink::WebVector<blink::WebMediaDeviceInfo> last_devices_;
+  PeerConnectionDependencyFactory* factory_;
+  MockMediaStreamVideoCapturerSource* video_source_;
 };
 
 class MediaStreamImplTest : public ::testing::Test {
  public:
   virtual void SetUp() {
     // Create our test object.
-    ms_dispatcher_.reset(new MockMediaStreamDispatcher());
-    dependency_factory_.reset(new MockMediaStreamDependencyFactory());
-    ms_impl_.reset(new MediaStreamImplUnderTest(ms_dispatcher_.get(),
-                                                dependency_factory_.get()));
+    child_process_.reset(new ChildProcess());
+    dependency_factory_.reset(new MockPeerConnectionDependencyFactory());
+    ms_dispatcher_ = new MockMediaStreamDispatcher();
+    ms_impl_.reset(new MediaStreamImplUnderTest(
+        dependency_factory_.get(),
+        scoped_ptr<MediaStreamDispatcher>(ms_dispatcher_).Pass()));
   }
 
   blink::WebMediaStream RequestLocalMediaStream() {
     ms_impl_->RequestUserMedia();
-    FakeMediaStreamDispatcherComplete();
-    ChangeVideoSourceStateToLive();
-    ChangeAudioSourceStateToLive();
+    FakeMediaStreamDispatcherRequestUserMediaComplete();
+    StartMockedVideoSource();
 
     EXPECT_EQ(MediaStreamImplUnderTest::REQUEST_SUCCEEDED,
               ms_impl_->request_state());
 
     blink::WebMediaStream desc = ms_impl_->last_generated_stream();
-    content::MediaStreamExtraData* extra_data =
-        static_cast<content::MediaStreamExtraData*>(desc.extraData());
-    if (!extra_data || !extra_data->stream().get()) {
+    content::MediaStream* native_stream =
+        content::MediaStream::GetMediaStream(desc);
+    if (!native_stream) {
       ADD_FAILURE();
       return desc;
     }
 
-    EXPECT_EQ(1u, extra_data->stream()->GetAudioTracks().size());
-    EXPECT_EQ(1u, extra_data->stream()->GetVideoTracks().size());
-    EXPECT_NE(extra_data->stream()->GetAudioTracks()[0]->id(),
-              extra_data->stream()->GetVideoTracks()[0]->id());
+    blink::WebVector<blink::WebMediaStreamTrack> audio_tracks;
+    desc.audioTracks(audio_tracks);
+    blink::WebVector<blink::WebMediaStreamTrack> video_tracks;
+    desc.videoTracks(video_tracks);
+
+    EXPECT_EQ(1u, audio_tracks.size());
+    EXPECT_EQ(1u, video_tracks.size());
+    EXPECT_NE(audio_tracks[0].id(), video_tracks[0].id());
     return desc;
   }
 
-  void FakeMediaStreamDispatcherComplete() {
-    ms_impl_->OnStreamGenerated(ms_dispatcher_->request_id(),
+  void FakeMediaStreamDispatcherRequestUserMediaComplete() {
+    // Audio request ID is used as the shared request ID.
+    ms_impl_->OnStreamGenerated(ms_dispatcher_->audio_input_request_id(),
                                 ms_dispatcher_->stream_label(),
-                                ms_dispatcher_->audio_array(),
+                                ms_dispatcher_->audio_input_array(),
                                 ms_dispatcher_->video_array());
   }
 
-  void ChangeVideoSourceStateToLive() {
-    if (dependency_factory_->last_video_source() != NULL) {
-      dependency_factory_->last_video_source()->SetLive();
-    }
+  void FakeMediaStreamDispatcherRequestMediaDevicesComplete() {
+    ms_impl_->OnDevicesEnumerated(ms_dispatcher_->audio_input_request_id(),
+                                  ms_dispatcher_->audio_input_array());
+    ms_impl_->OnDevicesEnumerated(ms_dispatcher_->audio_output_request_id(),
+                                  ms_dispatcher_->audio_output_array());
+    ms_impl_->OnDevicesEnumerated(ms_dispatcher_->video_request_id(),
+                                  ms_dispatcher_->video_array());
   }
 
-  void ChangeAudioSourceStateToLive() {
-    if (dependency_factory_->last_audio_source() != NULL) {
-      dependency_factory_->last_audio_source()->SetLive();
-    }
+  void StartMockedVideoSource() {
+    MockMediaStreamVideoCapturerSource* video_source =
+        ms_impl_->last_created_video_source();
+    if (video_source->SourceHasAttemptedToStart())
+      video_source->StartMockedSource();
   }
 
-  void ChangeVideoSourceStateToEnded() {
-    if (dependency_factory_->last_video_source() != NULL) {
-      dependency_factory_->last_video_source()->SetEnded();
-    }
+  void FailToStartMockedVideoSource() {
+    MockMediaStreamVideoCapturerSource* video_source =
+        ms_impl_->last_created_video_source();
+    if (video_source->SourceHasAttemptedToStart())
+      video_source->FailToStartMockedSource();
   }
 
-  void ChangeAudioSourceStateToEnded() {
-    if (dependency_factory_->last_audio_source() != NULL) {
-      dependency_factory_->last_audio_source()->SetEnded();
-    }
+  void FailToCreateNextAudioCapturer() {
+    dependency_factory_->FailToCreateNextAudioCapturer();
   }
 
  protected:
-  scoped_ptr<MockMediaStreamDispatcher> ms_dispatcher_;
+  base::MessageLoop message_loop_;
+  scoped_ptr<ChildProcess> child_process_;
+  MockMediaStreamDispatcher* ms_dispatcher_;  // Owned my |ms_impl_|.
   scoped_ptr<MediaStreamImplUnderTest> ms_impl_;
-  scoped_ptr<MockMediaStreamDependencyFactory> dependency_factory_;
+  scoped_ptr<MockPeerConnectionDependencyFactory> dependency_factory_;
 };
 
 TEST_F(MediaStreamImplTest, GenerateMediaStream) {
@@ -199,134 +282,207 @@ TEST_F(MediaStreamImplTest, GenerateTwoMediaStreamsWithDifferentSources) {
             desc2_audio_tracks[0].source().extraData());
 }
 
-TEST_F(MediaStreamImplTest, StopLocalMediaStream) {
+TEST_F(MediaStreamImplTest, StopLocalTracks) {
   // Generate a stream with both audio and video.
   blink::WebMediaStream mixed_desc = RequestLocalMediaStream();
 
-  // Stop generated local streams.
-  ms_impl_->OnLocalMediaStreamStop(mixed_desc.id().utf8());
+  blink::WebVector<blink::WebMediaStreamTrack> audio_tracks;
+  mixed_desc.audioTracks(audio_tracks);
+  MediaStreamTrack* audio_track = MediaStreamTrack::GetTrack(audio_tracks[0]);
+  audio_track->Stop();
   EXPECT_EQ(1, ms_dispatcher_->stop_audio_device_counter());
+
+  blink::WebVector<blink::WebMediaStreamTrack> video_tracks;
+  mixed_desc.videoTracks(video_tracks);
+  MediaStreamTrack* video_track = MediaStreamTrack::GetTrack(video_tracks[0]);
+  video_track->Stop();
   EXPECT_EQ(1, ms_dispatcher_->stop_video_device_counter());
 }
 
-// This test that a source is not stopped even if the MediaStream is stopped if
-// there are two MediaStreams using the same device. The source is stopped
-// if there are no more MediaStreams using the device.
-TEST_F(MediaStreamImplTest, StopLocalMediaStreamWhenTwoStreamUseSameDevices) {
+// This test that a source is not stopped even if the tracks in a
+// MediaStream is stopped if there are two MediaStreams with tracks using the
+// same device. The source is stopped
+// if there are no more MediaStream tracks using the device.
+TEST_F(MediaStreamImplTest, StopLocalTracksWhenTwoStreamUseSameDevices) {
   // Generate a stream with both audio and video.
   blink::WebMediaStream desc1 = RequestLocalMediaStream();
   blink::WebMediaStream desc2 = RequestLocalMediaStream();
 
-  ms_impl_->OnLocalMediaStreamStop(desc2.id().utf8());
+  blink::WebVector<blink::WebMediaStreamTrack> audio_tracks1;
+  desc1.audioTracks(audio_tracks1);
+  MediaStreamTrack* audio_track1 = MediaStreamTrack::GetTrack(audio_tracks1[0]);
+  audio_track1->Stop();
   EXPECT_EQ(0, ms_dispatcher_->stop_audio_device_counter());
-  EXPECT_EQ(0, ms_dispatcher_->stop_video_device_counter());
 
-  ms_impl_->OnLocalMediaStreamStop(desc1.id().utf8());
+  blink::WebVector<blink::WebMediaStreamTrack> audio_tracks2;
+  desc2.audioTracks(audio_tracks2);
+  MediaStreamTrack* audio_track2 = MediaStreamTrack::GetTrack(audio_tracks2[0]);
+  audio_track2->Stop();
   EXPECT_EQ(1, ms_dispatcher_->stop_audio_device_counter());
+
+  blink::WebVector<blink::WebMediaStreamTrack> video_tracks1;
+  desc1.videoTracks(video_tracks1);
+  MediaStreamTrack* video_track1 = MediaStreamTrack::GetTrack(video_tracks1[0]);
+  video_track1->Stop();
+  EXPECT_EQ(0, ms_dispatcher_->stop_video_device_counter());
+
+  blink::WebVector<blink::WebMediaStreamTrack> video_tracks2;
+  desc2.videoTracks(video_tracks2);
+  MediaStreamTrack* video_track2 = MediaStreamTrack::GetTrack(video_tracks2[0]);
+  video_track2->Stop();
   EXPECT_EQ(1, ms_dispatcher_->stop_video_device_counter());
 }
 
-// Test that the source is stopped even if there are two MediaStreams using
-// the same source.
-TEST_F(MediaStreamImplTest, StopSource) {
+TEST_F(MediaStreamImplTest, StopSourceWhenMediaStreamGoesOutOfScope) {
   // Generate a stream with both audio and video.
-  blink::WebMediaStream desc1 = RequestLocalMediaStream();
-  blink::WebMediaStream desc2 = RequestLocalMediaStream();
+  RequestLocalMediaStream();
+  // Makes sure the test itself don't hold a reference to the created
+  // MediaStream.
+  ms_impl_->ClearLastGeneratedStream();
 
-  // Stop the video source.
-  blink::WebVector<blink::WebMediaStreamTrack> video_tracks;
-  desc1.videoTracks(video_tracks);
-  ms_impl_->OnLocalSourceStop(video_tracks[0].source());
-  EXPECT_EQ(0, ms_dispatcher_->stop_audio_device_counter());
-  EXPECT_EQ(1, ms_dispatcher_->stop_video_device_counter());
-
-  // Stop the audio source.
-  blink::WebVector<blink::WebMediaStreamTrack> audio_tracks;
-  desc1.audioTracks(audio_tracks);
-  ms_impl_->OnLocalSourceStop(audio_tracks[0].source());
+  // Expect the sources to be stopped when the MediaStream goes out of scope.
   EXPECT_EQ(1, ms_dispatcher_->stop_audio_device_counter());
   EXPECT_EQ(1, ms_dispatcher_->stop_video_device_counter());
 }
 
-// Test that the MediaStreams are deleted if the owning WebFrame is deleted.
+// Test that the MediaStreams are deleted if the owning WebFrame is closing.
 // In the unit test the owning frame is NULL.
 TEST_F(MediaStreamImplTest, FrameWillClose) {
   // Test a stream with both audio and video.
   blink::WebMediaStream mixed_desc = RequestLocalMediaStream();
+  blink::WebMediaStream desc2 = RequestLocalMediaStream();
+  ms_impl_->FrameWillClose();
+  EXPECT_EQ(1, ms_dispatcher_->stop_audio_device_counter());
+  EXPECT_EQ(1, ms_dispatcher_->stop_video_device_counter());
+}
 
-  // Test that the MediaStreams are deleted if the owning WebFrame is deleted.
-  // In the unit test the owning frame is NULL.
-  ms_impl_->FrameWillClose(NULL);
+// This test what happens if a video source to a MediaSteam fails to start.
+TEST_F(MediaStreamImplTest, MediaVideoSourceFailToStart) {
+  ms_impl_->RequestUserMedia();
+  FakeMediaStreamDispatcherRequestUserMediaComplete();
+  FailToStartMockedVideoSource();
+  EXPECT_EQ(MediaStreamImplUnderTest::REQUEST_FAILED,
+            ms_impl_->request_state());
+  EXPECT_EQ(MEDIA_DEVICE_TRACK_START_FAILURE,
+            ms_impl_->error_reason());
+  EXPECT_EQ(1, ms_dispatcher_->request_stream_counter());
   EXPECT_EQ(1, ms_dispatcher_->stop_audio_device_counter());
   EXPECT_EQ(1, ms_dispatcher_->stop_video_device_counter());
 }
 
-// This test what happens if a source to a MediaSteam fails to start.
-TEST_F(MediaStreamImplTest, MediaSourceFailToStart) {
+// This test what happens if an audio source fail to initialize.
+TEST_F(MediaStreamImplTest, MediaAudioSourceFailToInitialize) {
+  FailToCreateNextAudioCapturer();
   ms_impl_->RequestUserMedia();
-  FakeMediaStreamDispatcherComplete();
-  ChangeVideoSourceStateToEnded();
-  ChangeAudioSourceStateToEnded();
+  FakeMediaStreamDispatcherRequestUserMediaComplete();
+  StartMockedVideoSource();
   EXPECT_EQ(MediaStreamImplUnderTest::REQUEST_FAILED,
             ms_impl_->request_state());
+  EXPECT_EQ(MEDIA_DEVICE_TRACK_START_FAILURE,
+            ms_impl_->error_reason());
   EXPECT_EQ(1, ms_dispatcher_->request_stream_counter());
   EXPECT_EQ(1, ms_dispatcher_->stop_audio_device_counter());
   EXPECT_EQ(1, ms_dispatcher_->stop_video_device_counter());
 }
 
-// This test what happens if MediaStreamImpl is deleted while the sources of a
-// MediaStream is being started.
+// This test what happens if MediaStreamImpl is deleted before a source has
+// started.
 TEST_F(MediaStreamImplTest, MediaStreamImplShutDown) {
   ms_impl_->RequestUserMedia();
-  FakeMediaStreamDispatcherComplete();
+  FakeMediaStreamDispatcherRequestUserMediaComplete();
   EXPECT_EQ(1, ms_dispatcher_->request_stream_counter());
   EXPECT_EQ(MediaStreamImplUnderTest::REQUEST_NOT_COMPLETE,
             ms_impl_->request_state());
   ms_impl_.reset();
-  ChangeAudioSourceStateToLive();
-  ChangeVideoSourceStateToLive();
 }
 
 // This test what happens if the WebFrame is closed while the MediaStream is
 // being generated by the MediaStreamDispatcher.
 TEST_F(MediaStreamImplTest, ReloadFrameWhileGeneratingStream) {
   ms_impl_->RequestUserMedia();
-  ms_impl_->FrameWillClose(NULL);
+  ms_impl_->FrameWillClose();
   EXPECT_EQ(1, ms_dispatcher_->request_stream_counter());
   EXPECT_EQ(0, ms_dispatcher_->stop_audio_device_counter());
   EXPECT_EQ(0, ms_dispatcher_->stop_video_device_counter());
-  ChangeAudioSourceStateToLive();
-  ChangeVideoSourceStateToLive();
   EXPECT_EQ(MediaStreamImplUnderTest::REQUEST_NOT_COMPLETE,
             ms_impl_->request_state());
 }
 
 // This test what happens if the WebFrame is closed while the sources are being
-// started by MediaStreamDependencyFactory.
+// started.
 TEST_F(MediaStreamImplTest, ReloadFrameWhileGeneratingSources) {
   ms_impl_->RequestUserMedia();
-  FakeMediaStreamDispatcherComplete();
+  FakeMediaStreamDispatcherRequestUserMediaComplete();
   EXPECT_EQ(1, ms_dispatcher_->request_stream_counter());
-  ms_impl_->FrameWillClose(NULL);
+  ms_impl_->FrameWillClose();
   EXPECT_EQ(1, ms_dispatcher_->stop_audio_device_counter());
   EXPECT_EQ(1, ms_dispatcher_->stop_video_device_counter());
-  ChangeAudioSourceStateToLive();
-  ChangeVideoSourceStateToLive();
   EXPECT_EQ(MediaStreamImplUnderTest::REQUEST_NOT_COMPLETE,
             ms_impl_->request_state());
 }
 
-// This test what happens if stop is called on a stream after the frame has
+// This test what happens if stop is called on a track after the frame has
 // been reloaded.
-TEST_F(MediaStreamImplTest, StopStreamAfterReload) {
+TEST_F(MediaStreamImplTest, StopTrackAfterReload) {
   blink::WebMediaStream mixed_desc = RequestLocalMediaStream();
   EXPECT_EQ(1, ms_dispatcher_->request_stream_counter());
-  ms_impl_->FrameWillClose(NULL);
+  ms_impl_->FrameWillClose();
   EXPECT_EQ(1, ms_dispatcher_->stop_audio_device_counter());
   EXPECT_EQ(1, ms_dispatcher_->stop_video_device_counter());
-  ms_impl_->OnLocalMediaStreamStop(mixed_desc.id().utf8());
+
+  blink::WebVector<blink::WebMediaStreamTrack> audio_tracks;
+  mixed_desc.audioTracks(audio_tracks);
+  MediaStreamTrack* audio_track = MediaStreamTrack::GetTrack(audio_tracks[0]);
+  audio_track->Stop();
   EXPECT_EQ(1, ms_dispatcher_->stop_audio_device_counter());
+
+  blink::WebVector<blink::WebMediaStreamTrack> video_tracks;
+  mixed_desc.videoTracks(video_tracks);
+  MediaStreamTrack* video_track = MediaStreamTrack::GetTrack(video_tracks[0]);
+  video_track->Stop();
   EXPECT_EQ(1, ms_dispatcher_->stop_video_device_counter());
 }
 
+TEST_F(MediaStreamImplTest, EnumerateMediaDevices) {
+  ms_impl_->RequestMediaDevices();
+  FakeMediaStreamDispatcherRequestMediaDevicesComplete();
+
+  EXPECT_EQ(MediaStreamImplUnderTest::REQUEST_SUCCEEDED,
+            ms_impl_->request_state());
+
+  // Audio input device with matched output ID.
+  EXPECT_FALSE(ms_impl_->last_devices()[0].deviceId().isEmpty());
+  EXPECT_EQ(blink::WebMediaDeviceInfo::MediaDeviceKindAudioInput,
+            ms_impl_->last_devices()[0].kind());
+  EXPECT_FALSE(ms_impl_->last_devices()[0].label().isEmpty());
+  EXPECT_FALSE(ms_impl_->last_devices()[0].groupId().isEmpty());
+
+  // Audio input device without matched output ID.
+  EXPECT_FALSE(ms_impl_->last_devices()[1].deviceId().isEmpty());
+  EXPECT_EQ(blink::WebMediaDeviceInfo::MediaDeviceKindAudioInput,
+            ms_impl_->last_devices()[1].kind());
+  EXPECT_FALSE(ms_impl_->last_devices()[1].label().isEmpty());
+  EXPECT_FALSE(ms_impl_->last_devices()[1].groupId().isEmpty());
+
+  // Video input device.
+  EXPECT_FALSE(ms_impl_->last_devices()[2].deviceId().isEmpty());
+  EXPECT_EQ(blink::WebMediaDeviceInfo::MediaDeviceKindVideoInput,
+            ms_impl_->last_devices()[2].kind());
+  EXPECT_FALSE(ms_impl_->last_devices()[2].label().isEmpty());
+  EXPECT_TRUE(ms_impl_->last_devices()[2].groupId().isEmpty());
+
+  // Audio output device.
+  EXPECT_FALSE(ms_impl_->last_devices()[3].deviceId().isEmpty());
+  EXPECT_EQ(blink::WebMediaDeviceInfo::MediaDeviceKindAudioOutput,
+            ms_impl_->last_devices()[3].kind());
+  EXPECT_FALSE(ms_impl_->last_devices()[3].label().isEmpty());
+  EXPECT_FALSE(ms_impl_->last_devices()[3].groupId().isEmpty());
+
+  // Verfify group IDs.
+  EXPECT_TRUE(ms_impl_->last_devices()[0].groupId().equals(
+                  ms_impl_->last_devices()[3].groupId()));
+  EXPECT_FALSE(ms_impl_->last_devices()[1].groupId().equals(
+                   ms_impl_->last_devices()[3].groupId()));
+}
+
 }  // namespace content