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

#include "components/mirroring/service/openscreen_stats_client.h"

#include "base/logging.h"
#include "media/cast/logging/stats_event_subscriber.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/openscreen/src/cast/streaming/public/sender_session.h"
#include "third_party/openscreen/src/cast/streaming/public/statistics.h"

namespace mirroring {

using openscreen::cast::SenderStats;
using openscreen::cast::StatisticType;
using HistogramsList = openscreen::cast::SenderStats::HistogramsList;
using StatisticsList = openscreen::cast::SenderStats::StatisticsList;
using SimpleHistogram = openscreen::cast::SimpleHistogram;
using CastStat = media::cast::StatsEventSubscriber::CastStat;

class OpenscreenStatsClientTest : public ::testing::Test {
 public:
  OpenscreenStatsClientTest()
      : test_statistics_list_(ConstructTestStatisticsList()),
        test_histogram_(ConstructTestHistogram()),
        test_histograms_list_(ConstructTestHistogramsList()),
        test_sender_stats_(ConstructTestSenderStats()),
        openscreen_stats_client_(std::make_unique<OpenscreenStatsClient>()) {}

  OpenscreenStatsClientTest(const OpenscreenStatsClientTest&) = delete;
  OpenscreenStatsClientTest& operator=(const OpenscreenStatsClientTest&) =
      delete;

  ~OpenscreenStatsClientTest() override = default;

  StatisticsList ConstructTestStatisticsList() {
    StatisticsList ret;
    for (std::size_t i = 0; i < ret.size(); ++i) {
      ret[i] = static_cast<double>(i);
    }
    return ret;
  }

  HistogramsList ConstructTestHistogramsList() {
    HistogramsList ret;
    for (auto& histogram : ret) {
      histogram = ConstructTestHistogram();
    }
    return ret;
  }

  SimpleHistogram ConstructTestHistogram() {
    SimpleHistogram ret = SimpleHistogram(0, 10, 2);
    ret.Add(1);
    ret.Add(4);
    ret.Add(7);
    return ret;
  }

  SenderStats ConstructTestSenderStats() {
    return SenderStats{.audio_statistics = ConstructTestStatisticsList(),
                       .audio_histograms = ConstructTestHistogramsList(),
                       .video_statistics = ConstructTestStatisticsList(),
                       .video_histograms = ConstructTestHistogramsList()};
  }

  SenderStats ConstructDefaultSenderStats() {
    return SenderStats{.audio_statistics = StatisticsList(),
                       .audio_histograms = HistogramsList(),
                       .video_statistics = StatisticsList(),
                       .video_histograms = HistogramsList()};
  }

 protected:
  StatisticsList test_statistics_list_;
  SimpleHistogram test_histogram_;
  HistogramsList test_histograms_list_;
  SenderStats test_sender_stats_;

  std::unique_ptr<OpenscreenStatsClient> openscreen_stats_client_;
};

TEST_F(OpenscreenStatsClientTest, OnStatisticsUpdatedValidValues) {
  // Test to verify that statistics are parsed and verified into both stats
  // and histograms.
  openscreen_stats_client_->OnStatisticsUpdated(test_sender_stats_);
  auto stats_dict = openscreen_stats_client_->GetStats();

  // Check that the GetStats() dict has been populated with audio stats and
  // histograms.
  const base::DictValue* audio_dict = stats_dict.FindDict(
      media::cast::StatsEventSubscriber::kAudioStatsDictKey);
  EXPECT_TRUE(audio_dict);
  EXPECT_TRUE(audio_dict->contains("NUM_FRAMES_CAPTURED"));
  EXPECT_TRUE(audio_dict->FindDouble("NUM_FRAMES_CAPTURED").has_value());

  EXPECT_TRUE(audio_dict->contains("NUM_FRAMES_DROPPED_BY_ENCODER"));
  EXPECT_TRUE(
      audio_dict->FindDouble("NUM_FRAMES_DROPPED_BY_ENCODER").has_value());

  EXPECT_TRUE(audio_dict->contains("AVG_CAPTURE_LATENCY_MS"));
  EXPECT_TRUE(audio_dict->FindDouble("AVG_CAPTURE_LATENCY_MS").has_value());

  // Check for some histograms.
  EXPECT_TRUE(audio_dict->contains("E2E_LATENCY_MS_HISTO"));
  EXPECT_TRUE(audio_dict->FindList("E2E_LATENCY_MS_HISTO")->size());

  EXPECT_TRUE(audio_dict->contains("NETWORK_LATENCY_MS_HISTO"));
  EXPECT_TRUE(audio_dict->FindList("NETWORK_LATENCY_MS_HISTO")->size());

  // Check that the GetStats() dict has been populated with video stats and
  // histograms.
  const base::DictValue* video_dict = stats_dict.FindDict(
      media::cast::StatsEventSubscriber::kVideoStatsDictKey);
  EXPECT_TRUE(video_dict);

  EXPECT_TRUE(video_dict->contains("NUM_FRAMES_CAPTURED"));
  EXPECT_TRUE(video_dict->FindDouble("NUM_FRAMES_CAPTURED").has_value());

  EXPECT_TRUE(video_dict->contains("NUM_FRAMES_DROPPED_BY_ENCODER"));
  EXPECT_TRUE(
      video_dict->FindDouble("NUM_FRAMES_DROPPED_BY_ENCODER").has_value());

  EXPECT_TRUE(video_dict->contains("AVG_CAPTURE_LATENCY_MS"));
  EXPECT_TRUE(video_dict->FindDouble("AVG_CAPTURE_LATENCY_MS").has_value());

  // Check for some histograms.
  EXPECT_TRUE(video_dict->contains("E2E_LATENCY_MS_HISTO"));
  EXPECT_TRUE(video_dict->FindList("E2E_LATENCY_MS_HISTO")->size());

  EXPECT_TRUE(video_dict->contains("NETWORK_LATENCY_MS_HISTO"));
  EXPECT_TRUE(video_dict->FindList("NETWORK_LATENCY_MS_HISTO")->size());
}

TEST_F(OpenscreenStatsClientTest, OnStatisticsUpdatedEmptyValues) {
  // Test to verify that statistics are parsed into both stats
  // and histograms even if they are empty.
  openscreen_stats_client_->OnStatisticsUpdated(ConstructDefaultSenderStats());
  auto stats_dict = openscreen_stats_client_->GetStats();

  // Check that the GetStats() dict has been populated with audio stats and
  // histograms.
  const base::DictValue* audio_dict = stats_dict.FindDict(
      media::cast::StatsEventSubscriber::kAudioStatsDictKey);
  EXPECT_TRUE(audio_dict);
  EXPECT_TRUE(audio_dict->contains("NUM_FRAMES_CAPTURED"));
  EXPECT_TRUE(audio_dict->FindDouble("NUM_FRAMES_CAPTURED").has_value());

  // Check for some histograms. They should have no size since they are empty.
  EXPECT_TRUE(audio_dict->contains("E2E_LATENCY_MS_HISTO"));
  EXPECT_FALSE(audio_dict->FindList("E2E_LATENCY_MS_HISTO")->size());

  EXPECT_TRUE(audio_dict->contains("NETWORK_LATENCY_MS_HISTO"));
  EXPECT_FALSE(audio_dict->FindList("NETWORK_LATENCY_MS_HISTO")->size());

  // Check that the GetStats() dict has been populated with video stats and
  // histograms.
  const base::DictValue* video_dict = stats_dict.FindDict(
      media::cast::StatsEventSubscriber::kVideoStatsDictKey);
  EXPECT_TRUE(video_dict);
  EXPECT_TRUE(video_dict->contains("NUM_FRAMES_CAPTURED"));
  EXPECT_TRUE(video_dict->FindDouble("NUM_FRAMES_CAPTURED").has_value());

  // Check for some histograms. They should have no size since they are empty.
  EXPECT_TRUE(video_dict->contains("E2E_LATENCY_MS_HISTO"));
  EXPECT_FALSE(video_dict->FindList("E2E_LATENCY_MS_HISTO")->size());

  EXPECT_TRUE(video_dict->contains("NETWORK_LATENCY_MS_HISTO"));
  EXPECT_FALSE(video_dict->FindList("NETWORK_LATENCY_MS_HISTO")->size());
}

}  // namespace mirroring
