// Copyright 2018 The Chromium Authors
// 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/strings/stringprintf.h"
#include "base/test/test_timeouts.h"
#include "base/time/time.h"
#include "chrome/browser/media/webrtc/test_stats_dictionary.h"
#include "chrome/browser/media/webrtc/webrtc_browsertest_base.h"
#include "chrome/browser/media/webrtc/webrtc_browsertest_common.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "media/base/media_switches.h"
#include "testing/perf/perf_test.h"
#include "ui/gl/gl_switches.h"

namespace {

static const char kMainWebrtcTestHtmlPage[] =
    "/webrtc/webrtc_video_display_perf_test.html";
static const char kInboundRtp[] = "inbound-rtp";
static const char kOutboundRtp[] = "outbound-rtp";

// Sums up "RTC[In/Out]boundRTPStreamStats.bytes_[received/sent]" values.
double GetTotalRTPStreamBytes(content::TestStatsReportDictionary* report,
                              const char* type,
                              const char* media_type) {
  DCHECK(type == kInboundRtp || type == kOutboundRtp);
  const char* bytes_name =
      (type == kInboundRtp) ? "bytesReceived" : "bytesSent";
  double total_bytes = 0.0;
  report->ForEach([&type, &bytes_name, &media_type,
                   &total_bytes](const content::TestStatsDictionary& stats) {
    if (stats.GetString("type") == type &&
        stats.GetString("mediaType") == media_type) {
      total_bytes += stats.GetNumber(bytes_name);
    }
  });
  return total_bytes;
}

double GetVideoBytesSent(content::TestStatsReportDictionary* report) {
  return GetTotalRTPStreamBytes(report, kOutboundRtp, "video");
}

double GetVideoBytesReceived(content::TestStatsReportDictionary* report) {
  return GetTotalRTPStreamBytes(report, kInboundRtp, "video");
}

}  // anonymous namespace

namespace content {

// Tests the performance of WebRTC peer connection with high bitrate
//
// This test creates a WebRTC peer connection between two tabs and sets a very
// high target bitrate to observe any perf regressions/improvements for such
// cases. In order to achieve this, we use a fake codec that creates a dummy
// output for the given bitrate.
class WebRtcVideoHighBitrateBrowserTest : public WebRtcTestBase {
 public:
  void SetUpInProcessBrowserTestFixture() override {
    DetectErrorsInJavaScript();
  }

  void SetUpCommandLine(base::CommandLine* command_line) override {
    command_line->AppendSwitch(switches::kUseFakeCodecForPeerConnection);
    command_line->AppendSwitch(switches::kUseFakeUIForMediaStream);
    command_line->AppendSwitch(switches::kUseGpuInTests);
  }

 protected:
  void SetDefaultVideoTargetBitrate(content::WebContents* tab,
                                    int bits_per_second) {
    EXPECT_EQ("ok", ExecuteJavascript(
                        base::StringPrintf("setDefaultVideoTargetBitrate(%d)",
                                           bits_per_second),
                        tab));
  }
};

IN_PROC_BROWSER_TEST_F(WebRtcVideoHighBitrateBrowserTest,
                       MANUAL_HighBitrateEncodeDecode) {
  ASSERT_TRUE(embedded_test_server()->Start());
  ASSERT_GE(TestTimeouts::test_launcher_timeout().InSeconds(), 30)
      << "This is a long-running test; you must specify "
         "--test-launcher-timeout to have a value of at least 30000.";
  ASSERT_GE(TestTimeouts::action_max_timeout().InSeconds(), 30)
      << "This is a long-running test; you must specify "
         "--ui-test-action-max-timeout to have a value of at least 30000.";
  ASSERT_LT(TestTimeouts::action_max_timeout(),
            TestTimeouts::test_launcher_timeout())
      << "action_max_timeout needs to be strictly-less-than "
         "test_launcher_timeout";

  content::WebContents* left_tab =
      OpenPageAndGetUserMediaInNewTabWithConstraints(
          embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage),
          "{audio: true, video: true}");
  content::WebContents* right_tab =
      OpenPageAndGetUserMediaInNewTabWithConstraints(
          embedded_test_server()->GetURL(kMainWebrtcTestHtmlPage),
          "{audio: true, video: false}");
  SetupPeerconnectionWithLocalStream(left_tab);
  SetupPeerconnectionWithLocalStream(right_tab);
  const int target_bits_per_second = 80000;
  SetDefaultVideoTargetBitrate(left_tab, target_bits_per_second);
  SetDefaultVideoTargetBitrate(right_tab, target_bits_per_second);
  NegotiateCall(left_tab, right_tab);

  // Run the connection a bit to ramp up.
  test::SleepInJavascript(left_tab, 10000);

  scoped_refptr<TestStatsReportDictionary> sender_report =
      GetStatsReportDictionary(left_tab);
  const double video_bytes_sent_before = GetVideoBytesSent(sender_report.get());
  scoped_refptr<TestStatsReportDictionary> receiver_report =
      GetStatsReportDictionary(right_tab);
  const double video_bytes_received_before =
      GetVideoBytesReceived(receiver_report.get());

  // Collect stats.
  const double duration_in_seconds = 5.0;
  test::SleepInJavascript(
      left_tab, duration_in_seconds * base::Time::kMillisecondsPerSecond);

  sender_report = GetStatsReportDictionary(left_tab);
  const double video_bytes_sent_after = GetVideoBytesSent(sender_report.get());
  receiver_report = GetStatsReportDictionary(right_tab);
  const double video_bytes_received_after =
      GetVideoBytesReceived(receiver_report.get());

  const double video_send_rate =
      (video_bytes_sent_after - video_bytes_sent_before) / duration_in_seconds;
  const double video_receive_rate =
      (video_bytes_received_after - video_bytes_received_before) /
      duration_in_seconds;

  perf_test::PrintResult("video", "", "send_rate", video_send_rate,
                         "bytes/second", false);
  perf_test::PrintResult("video", "", "receive_rate", video_receive_rate,
                         "bytes/second", false);

  HangUp(left_tab);
  HangUp(right_tab);
}

}  // namespace content
