root/content/browser/media/webrtc_getusermedia_browsertest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GenerateGetUserMediaWithMandatorySourceID
  2. GenerateGetUserMediaWithOptionalSourceID
  3. SetUpCommandLine
  4. StartTracing
  5. StopTracing
  6. OnTraceDataCollected
  7. CreateTraceAnalyzer
  8. RunGetUserMediaAndCollectMeasures
  9. GetSources
  10. IN_PROC_BROWSER_TEST_P
  11. IN_PROC_BROWSER_TEST_P
  12. IN_PROC_BROWSER_TEST_P
  13. IN_PROC_BROWSER_TEST_P
  14. IN_PROC_BROWSER_TEST_P
  15. IN_PROC_BROWSER_TEST_P
  16. IN_PROC_BROWSER_TEST_P
  17. IN_PROC_BROWSER_TEST_P
  18. IN_PROC_BROWSER_TEST_P
  19. IN_PROC_BROWSER_TEST_P
  20. IN_PROC_BROWSER_TEST_P
  21. IN_PROC_BROWSER_TEST_P
  22. IN_PROC_BROWSER_TEST_P
  23. IN_PROC_BROWSER_TEST_P
  24. IN_PROC_BROWSER_TEST_P
  25. IN_PROC_BROWSER_TEST_P
  26. IN_PROC_BROWSER_TEST_P
  27. user_media
  28. IN_PROC_BROWSER_TEST_P

// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/command_line.h"
#include "base/debug/trace_event_impl.h"
#include "base/json/json_reader.h"
#include "base/strings/stringprintf.h"
#include "base/test/trace_event_analyzer.h"
#include "base/values.h"
#include "content/browser/media/webrtc_internals.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/test/webrtc_content_browsertest_base.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/perf/perf_test.h"

#if defined(OS_WIN)
#include "base/win/windows_version.h"
#endif

using trace_analyzer::TraceAnalyzer;
using trace_analyzer::Query;
using trace_analyzer::TraceEventVector;

namespace {

static const char kGetUserMediaAndStop[] = "getUserMediaAndStop";
static const char kGetUserMediaAndGetStreamUp[] = "getUserMediaAndGetStreamUp";
static const char kGetUserMediaAndAnalyseAndStop[] =
    "getUserMediaAndAnalyseAndStop";
static const char kGetUserMediaAndExpectFailure[] =
    "getUserMediaAndExpectFailure";
static const char kRenderSameTrackMediastreamAndStop[] =
    "renderSameTrackMediastreamAndStop";
static const char kRenderClonedMediastreamAndStop[] =
    "renderClonedMediastreamAndStop";
static const char kRenderClonedTrackMediastreamAndStop[] =
    "renderClonedTrackMediastreamAndStop";
static const char kRenderDuplicatedMediastreamAndStop[] =
    "renderDuplicatedMediastreamAndStop";

// Results returned by JS.
static const char kOK[] = "OK";

std::string GenerateGetUserMediaWithMandatorySourceID(
    const std::string& function_name,
    const std::string& audio_source_id,
    const std::string& video_source_id) {
  const std::string audio_constraint =
      "audio: {mandatory: { sourceId:\"" + audio_source_id + "\"}}, ";

  const std::string video_constraint =
      "video: {mandatory: { sourceId:\"" + video_source_id + "\"}}";
  return function_name + "({" + audio_constraint + video_constraint + "});";
}

std::string GenerateGetUserMediaWithOptionalSourceID(
    const std::string& function_name,
    const std::string& audio_source_id,
    const std::string& video_source_id) {
  const std::string audio_constraint =
      "audio: {optional: [{sourceId:\"" + audio_source_id + "\"}]}, ";

  const std::string video_constraint =
      "video: {optional: [{ sourceId:\"" + video_source_id + "\"}]}";
  return function_name + "({" + audio_constraint + video_constraint + "});";
}

}  // namespace

namespace content {

class WebRtcGetUserMediaBrowserTest: public WebRtcContentBrowserTest,
                                     public testing::WithParamInterface<bool> {
 public:
  WebRtcGetUserMediaBrowserTest() : trace_log_(NULL) {}
  virtual ~WebRtcGetUserMediaBrowserTest() {}

  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    WebRtcContentBrowserTest::SetUpCommandLine(command_line);

    bool enable_audio_track_processing = GetParam();
    if (enable_audio_track_processing)
      command_line->AppendSwitch(switches::kEnableAudioTrackProcessing);
  }

  void StartTracing() {
    CHECK(trace_log_ == NULL) << "Can only can start tracing once";
    trace_log_ = base::debug::TraceLog::GetInstance();
    trace_log_->SetEnabled(base::debug::CategoryFilter("video"),
                           base::debug::TraceLog::RECORDING_MODE,
                           base::debug::TraceLog::ENABLE_SAMPLING);
    // Check that we are indeed recording.
    EXPECT_EQ(trace_log_->GetNumTracesRecorded(), 1);
  }

  void StopTracing() {
    CHECK(message_loop_runner_ == NULL) << "Calling StopTracing more than once";
    trace_log_->SetDisabled();
    message_loop_runner_ = new MessageLoopRunner;
    trace_log_->Flush(base::Bind(
        &WebRtcGetUserMediaBrowserTest::OnTraceDataCollected,
        base::Unretained(this)));
    message_loop_runner_->Run();
  }

  void OnTraceDataCollected(
      const scoped_refptr<base::RefCountedString>& events_str_ptr,
      bool has_more_events) {
    CHECK(!has_more_events);
    recorded_trace_data_ = events_str_ptr;
    message_loop_runner_->Quit();
  }

  TraceAnalyzer* CreateTraceAnalyzer() {
    return TraceAnalyzer::Create("[" + recorded_trace_data_->data() + "]");
  }

  void RunGetUserMediaAndCollectMeasures(const int time_to_sample_secs,
                                         const std::string& measure_filter,
                                         const std::string& graph_name) {
    ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());

    GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
    NavigateToURL(shell(), url);

    // Put getUserMedia to work and let it run for a couple of seconds.
    DCHECK(time_to_sample_secs);
    ExecuteJavascriptAndWaitForOk(
        base::StringPrintf("%s({video: true});",
                           kGetUserMediaAndGetStreamUp));

    // Now the stream is up and running, start collecting traces.
    StartTracing();

    // Let the stream run for a while in javascript.
    ExecuteJavascriptAndWaitForOk(
        base::StringPrintf("waitAndStopVideoTrack(%d);", time_to_sample_secs));

    // Wait until the page title changes to "OK". Do not sleep() here since that
    // would stop both this code and the browser underneath.
    StopTracing();

    scoped_ptr<TraceAnalyzer> analyzer(CreateTraceAnalyzer());
    analyzer->AssociateBeginEndEvents();
    trace_analyzer::TraceEventVector events;
    DCHECK(measure_filter.size());
    analyzer->FindEvents(
        Query::EventNameIs(measure_filter),
        &events);
    ASSERT_GT(events.size(), 0u)
        << "Could not collect any samples during test, this is bad";

    std::string duration_us;
    std::string interarrival_us;
    for (size_t i = 0; i != events.size(); ++i) {
      duration_us.append(
          base::StringPrintf("%d,", static_cast<int>(events[i]->duration)));
    }

    for (size_t i = 1; i < events.size(); ++i) {
      // The event |timestamp| comes in ns, divide to get us like |duration|.
      interarrival_us.append(base::StringPrintf("%d,",
          static_cast<int>((events[i]->timestamp - events[i - 1]->timestamp) /
                           base::Time::kNanosecondsPerMicrosecond)));
    }

    perf_test::PrintResultList(
        graph_name, "", "sample_duration", duration_us, "us", true);

    perf_test::PrintResultList(
        graph_name, "", "interarrival_time", interarrival_us, "us", true);
  }

  void GetSources(std::vector<std::string>* audio_ids,
                  std::vector<std::string>* video_ids) {
    GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
    NavigateToURL(shell(), url);

    std::string sources_as_json = ExecuteJavascriptAndReturnResult(
        "getSources()");
    EXPECT_FALSE(sources_as_json.empty());

    int error_code;
    std::string error_message;
    scoped_ptr<base::Value> value(
        base::JSONReader::ReadAndReturnError(sources_as_json,
                                             base::JSON_ALLOW_TRAILING_COMMAS,
                                             &error_code,
                                             &error_message));

    ASSERT_TRUE(value.get() != NULL) << error_message;
    EXPECT_EQ(value->GetType(), base::Value::TYPE_LIST);

    base::ListValue* values;
    ASSERT_TRUE(value->GetAsList(&values));

    for (base::ListValue::iterator it = values->begin();
         it != values->end(); ++it) {
      const base::DictionaryValue* dict;
      std::string kind;
      std::string id;
      ASSERT_TRUE((*it)->GetAsDictionary(&dict));
      ASSERT_TRUE(dict->GetString("kind", &kind));
      ASSERT_TRUE(dict->GetString("id", &id));
      ASSERT_FALSE(id.empty());
      EXPECT_TRUE(kind == "audio" || kind == "video");
      if (kind == "audio") {
        audio_ids->push_back(id);
      } else if (kind == "video") {
        video_ids->push_back(id);
      }
    }
    ASSERT_FALSE(audio_ids->empty());
    ASSERT_FALSE(video_ids->empty());
  }

 private:
  base::debug::TraceLog* trace_log_;
  scoped_refptr<base::RefCountedString> recorded_trace_data_;
  scoped_refptr<MessageLoopRunner> message_loop_runner_;
};

static const bool kRunTestsWithFlag[] = { false, true };
INSTANTIATE_TEST_CASE_P(WebRtcGetUserMediaBrowserTests,
                        WebRtcGetUserMediaBrowserTest,
                        testing::ValuesIn(kRunTestsWithFlag));

// These tests will all make a getUserMedia call with different constraints and
// see that the success callback is called. If the error callback is called or
// none of the callbacks are called the tests will simply time out and fail.
IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, GetVideoStreamAndStop) {
  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());

  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
  NavigateToURL(shell(), url);

  ExecuteJavascriptAndWaitForOk(
      base::StringPrintf("%s({video: true});", kGetUserMediaAndStop));
}

IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
                       RenderSameTrackMediastreamAndStop) {
  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());

  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
  NavigateToURL(shell(), url);

  ExecuteJavascriptAndWaitForOk(
      base::StringPrintf("%s({video: true});",
                         kRenderSameTrackMediastreamAndStop));
}

IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
                       RenderClonedMediastreamAndStop) {
  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());

  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
  NavigateToURL(shell(), url);


  ExecuteJavascriptAndWaitForOk(
      base::StringPrintf("%s({video: true});",
                         kRenderClonedMediastreamAndStop));
}

IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
                       kRenderClonedTrackMediastreamAndStop) {
  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());

  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
  NavigateToURL(shell(), url);

  ExecuteJavascriptAndWaitForOk(
      base::StringPrintf("%s({video: true});",
                         kRenderClonedTrackMediastreamAndStop));
}

IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
                       kRenderDuplicatedMediastreamAndStop) {
  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());

  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
  NavigateToURL(shell(), url);

  ExecuteJavascriptAndWaitForOk(
      base::StringPrintf("%s({video: true});",
                          kRenderDuplicatedMediastreamAndStop));
}

IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
                       GetAudioAndVideoStreamAndStop) {
  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());

  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
  NavigateToURL(shell(), url);

  ExecuteJavascriptAndWaitForOk(base::StringPrintf(
      "%s({video: true, audio: true});", kGetUserMediaAndStop));
}

IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
                       GetAudioAndVideoStreamAndClone) {
  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());

  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
  NavigateToURL(shell(), url);

  ExecuteJavascriptAndWaitForOk("getUserMediaAndClone();");
}

IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
                       RenderVideoTrackInMultipleTagsAndPause) {
  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());

  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
  NavigateToURL(shell(), url);

  ExecuteJavascriptAndWaitForOk("getUserMediaAndRenderInSeveralVideoTags();");
}



IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
                       GetUserMediaWithMandatorySourceID) {
  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());

  std::vector<std::string> audio_ids;
  std::vector<std::string> video_ids;
  GetSources(&audio_ids, &video_ids);

  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));

  // Test all combinations of mandatory sourceID;
  for (std::vector<std::string>::const_iterator video_it = video_ids.begin();
       video_it != video_ids.end(); ++video_it) {
    for (std::vector<std::string>::const_iterator audio_it = audio_ids.begin();
         audio_it != audio_ids.end(); ++audio_it) {
      NavigateToURL(shell(), url);
      EXPECT_EQ(kOK, ExecuteJavascriptAndReturnResult(
          GenerateGetUserMediaWithMandatorySourceID(
              kGetUserMediaAndStop,
              *audio_it,
              *video_it)));
    }
  }
}

IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
                       GetUserMediaWithInvalidMandatorySourceID) {
  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());

  std::vector<std::string> audio_ids;
  std::vector<std::string> video_ids;
  GetSources(&audio_ids, &video_ids);

  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));

  // Test with invalid mandatory audio sourceID.
  NavigateToURL(shell(), url);
  EXPECT_EQ("DevicesNotFoundError", ExecuteJavascriptAndReturnResult(
      GenerateGetUserMediaWithMandatorySourceID(
          kGetUserMediaAndExpectFailure,
          "something invalid",
          video_ids[0])));

  // Test with invalid mandatory video sourceID.
  EXPECT_EQ("DevicesNotFoundError", ExecuteJavascriptAndReturnResult(
      GenerateGetUserMediaWithMandatorySourceID(
          kGetUserMediaAndExpectFailure,
          audio_ids[0],
          "something invalid")));

  // Test with empty mandatory audio sourceID.
  EXPECT_EQ("DevicesNotFoundError", ExecuteJavascriptAndReturnResult(
      GenerateGetUserMediaWithMandatorySourceID(
          kGetUserMediaAndExpectFailure,
          "",
          video_ids[0])));
}

IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
                       GetUserMediaWithInvalidOptionalSourceID) {
  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());

  std::vector<std::string> audio_ids;
  std::vector<std::string> video_ids;
  GetSources(&audio_ids, &video_ids);

  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));

  // Test with invalid optional audio sourceID.
  NavigateToURL(shell(), url);
  EXPECT_EQ(kOK, ExecuteJavascriptAndReturnResult(
      GenerateGetUserMediaWithOptionalSourceID(
          kGetUserMediaAndStop,
          "something invalid",
          video_ids[0])));

  // Test with invalid optional video sourceID.
  EXPECT_EQ(kOK, ExecuteJavascriptAndReturnResult(
      GenerateGetUserMediaWithOptionalSourceID(
          kGetUserMediaAndStop,
          audio_ids[0],
          "something invalid")));

  // Test with empty optional audio sourceID.
  EXPECT_EQ(kOK, ExecuteJavascriptAndReturnResult(
      GenerateGetUserMediaWithOptionalSourceID(
          kGetUserMediaAndStop,
          "",
          video_ids[0])));
}

IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest, TwoGetUserMediaAndStop) {
  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());

  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));
  NavigateToURL(shell(), url);

  ExecuteJavascriptAndWaitForOk(
      "twoGetUserMediaAndStop({video: true, audio: true});");
}

IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
                       GetUserMediaWithTooHighVideoConstraintsValues) {
  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());

  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));

  int large_value = 99999;
  std::string call = GenerateGetUserMediaCall(kGetUserMediaAndExpectFailure,
                                              large_value,
                                              large_value,
                                              large_value,
                                              large_value,
                                              large_value,
                                              large_value);
  NavigateToURL(shell(), url);

  // TODO(perkj): A proper error code should be returned by gUM.
  EXPECT_EQ("TrackStartError", ExecuteJavascriptAndReturnResult(call));
}

// This test will make a simple getUserMedia page, verify that video is playing
// in a simple local <video>, and for a couple of seconds, collect some
// performance traces from VideoCaptureController colorspace conversion and
// potential resizing.
IN_PROC_BROWSER_TEST_P(
    WebRtcGetUserMediaBrowserTest,
    TraceVideoCaptureControllerPerformanceDuringGetUserMedia) {
  RunGetUserMediaAndCollectMeasures(
      10,
      "VideoCaptureController::OnIncomingCapturedData",
      "VideoCaptureController");
}

// This test calls getUserMedia and checks for aspect ratio behavior.
IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
                       TestGetUserMediaAspectRatio4To3) {
  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());

  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));

  std::string constraints_4_3 = GenerateGetUserMediaCall(
      kGetUserMediaAndAnalyseAndStop, 640, 640, 480, 480, 10, 30);

  NavigateToURL(shell(), url);
  ASSERT_EQ("w=640:h=480",
            ExecuteJavascriptAndReturnResult(constraints_4_3));
}

// This test calls getUserMedia and checks for aspect ratio behavior.
IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
                       TestGetUserMediaAspectRatio16To9) {
  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());

  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));

  std::string constraints_16_9 = GenerateGetUserMediaCall(
      kGetUserMediaAndAnalyseAndStop, 640, 640, 360, 360, 10, 30);

  NavigateToURL(shell(), url);
  ASSERT_EQ("w=640:h=360",
            ExecuteJavascriptAndReturnResult(constraints_16_9));
}

// This test calls getUserMedia and checks for aspect ratio behavior.
IN_PROC_BROWSER_TEST_P(WebRtcGetUserMediaBrowserTest,
                       TestGetUserMediaAspectRatio1To1) {
  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());

  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));

  std::string constraints_1_1 = GenerateGetUserMediaCall(
      kGetUserMediaAndAnalyseAndStop, 320, 320, 320, 320, 10, 30);

  NavigateToURL(shell(), url);
  ASSERT_EQ("w=320:h=320",
            ExecuteJavascriptAndReturnResult(constraints_1_1));
}

namespace {

struct UserMediaSizes {
  int min_width;
  int max_width;
  int min_height;
  int max_height;
  int min_frame_rate;
  int max_frame_rate;
};

}  // namespace

class WebRtcConstraintsBrowserTest
    : public WebRtcContentBrowserTest,
      public testing::WithParamInterface<UserMediaSizes> {
 public:
  WebRtcConstraintsBrowserTest() : user_media_(GetParam()) {}
  const UserMediaSizes& user_media() const { return user_media_; }

 private:
  UserMediaSizes user_media_;
};

// This test calls getUserMedia in sequence with different constraints.
IN_PROC_BROWSER_TEST_P(WebRtcConstraintsBrowserTest, GetUserMediaConstraints) {
  ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());

  GURL url(embedded_test_server()->GetURL("/media/getusermedia.html"));

  std::string call = GenerateGetUserMediaCall(kGetUserMediaAndStop,
                                              user_media().min_width,
                                              user_media().max_width,
                                              user_media().min_height,
                                              user_media().max_height,
                                              user_media().min_frame_rate,
                                              user_media().max_frame_rate);
  DVLOG(1) << "Calling getUserMedia: " << call;
  NavigateToURL(shell(), url);
  ExecuteJavascriptAndWaitForOk(call);
}

static const UserMediaSizes kAllUserMediaSizes[] = {
    {320, 320, 180, 180, 10, 30},
    {320, 320, 240, 240, 10, 30},
    {640, 640, 360, 360, 10, 30},
    {640, 640, 480, 480, 10, 30},
    {960, 960, 720, 720, 10, 30},
    {1280, 1280, 720, 720, 10, 30}};

INSTANTIATE_TEST_CASE_P(UserMedia,
                        WebRtcConstraintsBrowserTest,
                        testing::ValuesIn(kAllUserMediaSizes));

}  // namespace content

/* [<][>][^][v][top][bottom][index][help] */