#include "content/browser/compute_pressure/compute_pressure_manager.h"

#include <memory>
#include <utility>

#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "content/browser/compute_pressure/compute_pressure_test_support.h"
#include "content/public/browser/global_routing_id.h"
#include "content/test/test_render_frame_host.h"
#include "content/test/test_web_contents.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/test_support/fake_message_dispatch_context.h"
#include "mojo/public/cpp/test_support/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/mojom/compute_pressure/compute_pressure.mojom.h"

namespace content {

class ComputePressureManagerTest : public RenderViewHostImplTestHarness {
 public:
  void SetUp() override {
    RenderViewHostImplTestHarness::SetUp();
    NavigateAndCommit(kTestUrl);

    SetManager(ComputePressureManager::CreateForTesting(
        std::make_unique<FakeCpuProbe>(), base::Milliseconds(1),
        base::Milliseconds(1)));
  }

  void TearDown() override {

    manager_.reset();
    task_environment()->RunUntilIdle();

    RenderViewHostImplTestHarness::TearDown();
  }

  void SetManager(std::unique_ptr<ComputePressureManager> new_manager) {
    manager_ = std::move(new_manager);
    int process_id = main_rfh()->GetProcess()->GetID();
    int routing_id = main_rfh()->GetRoutingID();
    main_frame_id_ = GlobalRenderFrameHostId(process_id, routing_id);

    main_host_.reset();
    manager_->BindReceiver(kTestOrigin, main_frame_id_,
                           main_host_.BindNewPipeAndPassReceiver());
    main_host_sync_ =
        std::make_unique<ComputePressureHostSync>(main_host_.get());
  }

 protected:
  const GURL kTestUrl{"https:
  const url::Origin kTestOrigin =
      url::Origin::Create(GURL("https:
  const url::Origin kInsecureOrigin =
      url::Origin::Create(GURL("http:
  const url::Origin kThirdPartyOrigin =
      url::Origin::Create(GURL("https:

  const blink::mojom::ComputePressureQuantization kQuantization = {
      {0.2, 0.5, 0.8},
      {0.5}};

  base::test::ScopedFeatureList scoped_feature_list_;

  GlobalRenderFrameHostId main_frame_id_;

  std::unique_ptr<ComputePressureManager> manager_;
  mojo::Remote<blink::mojom::ComputePressureHost> main_host_;
  std::unique_ptr<ComputePressureHostSync> main_host_sync_;
};

#if defined(OS_FUCHSIA) && defined(_DEBUG) && defined(ARCH_CPU_ARM64)
#define MAYBE_OneObserver DISABLED_OneObserver
#elif defined(OS_LINUX) && defined(USE_OZONE)  
#define MAYBE_OneObserver DISABLED_OneObserver
#else
#define MAYBE_OneObserver OneObserver
#endif
TEST_F(ComputePressureManagerTest, MAYBE_OneObserver) {
  FakeComputePressureObserver observer;
  ASSERT_EQ(main_host_sync_->AddObserver(kQuantization,
                                         observer.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kOk);

  observer.WaitForUpdate();
  EXPECT_THAT(observer.updates(), testing::SizeIs(testing::Ge(1u)));
  EXPECT_THAT(
      observer.updates(),
      testing::Contains(blink::mojom::ComputePressureState(0.35, 0.75)));
}

#if defined(OS_FUCHSIA) && defined(_DEBUG) && defined(ARCH_CPU_ARM64)
#define MAYBE_ThreeObservers DISABLED_ThreeObservers
#elif defined(OS_LINUX) && defined(USE_OZONE)  
#define MAYBE_ThreeObservers DISABLED_ThreeObservers
#else
#define MAYBE_ThreeObservers ThreeObservers
#endif
TEST_F(ComputePressureManagerTest, MAYBE_ThreeObservers) {
  FakeComputePressureObserver observer1;
  ASSERT_EQ(main_host_sync_->AddObserver(kQuantization,
                                         observer1.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kOk);
  FakeComputePressureObserver observer2;
  ASSERT_EQ(main_host_sync_->AddObserver(kQuantization,
                                         observer2.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kOk);
  FakeComputePressureObserver observer3;
  ASSERT_EQ(main_host_sync_->AddObserver(kQuantization,
                                         observer3.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kOk);

  FakeComputePressureObserver::WaitForUpdates(
      {&observer1, &observer2, &observer3});

  EXPECT_THAT(observer1.updates(), testing::SizeIs(testing::Ge(1u)));
  EXPECT_THAT(
      observer1.updates(),
      testing::Contains(blink::mojom::ComputePressureState(0.35, 0.75)));
  EXPECT_THAT(observer2.updates(), testing::SizeIs(testing::Ge(1u)));
  EXPECT_THAT(
      observer2.updates(),
      testing::Contains(blink::mojom::ComputePressureState(0.35, 0.75)));
  EXPECT_THAT(observer3.updates(), testing::SizeIs(testing::Ge(1u)));
  EXPECT_THAT(
      observer3.updates(),
      testing::Contains(blink::mojom::ComputePressureState(0.35, 0.75)));
}

TEST_F(ComputePressureManagerTest, AddObserver_ThirdPartyFrame) {
  mojo::Remote<blink::mojom::ComputePressureHost> third_party_host;
  manager_->BindReceiver(kThirdPartyOrigin, main_frame_id_,
                         third_party_host.BindNewPipeAndPassReceiver());
  auto third_party_host_sync =
      std::make_unique<ComputePressureHostSync>(third_party_host.get());

  FakeComputePressureObserver third_party_observer;
  EXPECT_EQ(third_party_host_sync->AddObserver(
                kQuantization, third_party_observer.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kSecurityError);
  FakeComputePressureObserver first_party_observer;
  EXPECT_EQ(main_host_sync_->AddObserver(
                kQuantization, first_party_observer.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kOk);
}

TEST_F(ComputePressureManagerTest, AddObserver_NoVisibility) {
  test_rvh()->SimulateWasHidden();

  FakeComputePressureObserver observer;
  EXPECT_EQ(main_host_sync_->AddObserver(kQuantization,
                                         observer.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kOk);
}

TEST_F(ComputePressureManagerTest, AddObserver_InvalidFrame) {
  GlobalRenderFrameHostId invalid_routing_id(
      main_rfh()->GetProcess()->GetID() + 1, main_rfh()->GetRoutingID() + 1);
  mojo::Remote<blink::mojom::ComputePressureHost> invalid_host;
  manager_->BindReceiver(kTestOrigin, invalid_routing_id,
                         invalid_host.BindNewPipeAndPassReceiver());
  auto invalid_host_sync =
      std::make_unique<ComputePressureHostSync>(invalid_host.get());
  FakeComputePressureObserver invalid_observer;
  EXPECT_EQ(invalid_host_sync->AddObserver(
                kQuantization, invalid_observer.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kSecurityError);
  FakeComputePressureObserver valid_observer;
  EXPECT_EQ(main_host_sync_->AddObserver(
                kQuantization, valid_observer.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kOk);
}

TEST_F(ComputePressureManagerTest, AddObserver_InvalidQuantization) {
  FakeComputePressureObserver invalid_observer;
  blink::mojom::ComputePressureQuantization invalid_quantization(
      std::vector<double>{-1.0}, std::vector<double>{0.5});

  {
    mojo::test::BadMessageObserver bad_message_observer;
    EXPECT_EQ(
        main_host_sync_->AddObserver(
            invalid_quantization, invalid_observer.BindNewPipeAndPassRemote()),
        blink::mojom::ComputePressureStatus::kSecurityError);
    EXPECT_EQ("Invalid quantization", bad_message_observer.WaitForBadMessage());
  }

  FakeComputePressureObserver valid_observer;
  EXPECT_EQ(main_host_sync_->AddObserver(
                kQuantization, valid_observer.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kOk);
}

TEST_F(ComputePressureManagerTest, AddObserver_InsecureOrigin) {
  {
    mojo::FakeMessageDispatchContext fake_dispatch_context;
    mojo::test::BadMessageObserver bad_message_observer;
    mojo::Remote<blink::mojom::ComputePressureHost> insecure_host;
    manager_->BindReceiver(kInsecureOrigin, main_frame_id_,
                           insecure_host.BindNewPipeAndPassReceiver());
    EXPECT_EQ("Compute Pressure access from an insecure origin",
              bad_message_observer.WaitForBadMessage());
  }

  FakeComputePressureObserver valid_observer;
  EXPECT_EQ(main_host_sync_->AddObserver(
                kQuantization, valid_observer.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kOk);
}

TEST_F(ComputePressureManagerTest, AddObserver_NoProbe) {
  SetManager(ComputePressureManager::CreateForTesting(
      nullptr, base::Milliseconds(1), base::Milliseconds(1)));

  FakeComputePressureObserver observer;
  EXPECT_EQ(main_host_sync_->AddObserver(kQuantization,
                                         observer.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kNotSupported);
}

TEST_F(ComputePressureManagerTest, AddObserver_NoFeature) {
  scoped_feature_list_.Reset();
  scoped_feature_list_.InitAndDisableFeature(blink::features::kComputePressure);

  mojo::FakeMessageDispatchContext fake_dispatch_context;
  mojo::test::BadMessageObserver bad_message_observer;
  mojo::Remote<blink::mojom::ComputePressureHost> insecure_host;
  manager_->BindReceiver(kInsecureOrigin, main_frame_id_,
                         insecure_host.BindNewPipeAndPassReceiver());
  EXPECT_EQ("Compute Pressure not enabled",
            bad_message_observer.WaitForBadMessage());
}

}
// Copyright 2021 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 "content/browser/compute_pressure/compute_pressure_manager.h"

#include <memory>
#include <utility>

#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "content/browser/compute_pressure/compute_pressure_test_support.h"
#include "content/public/browser/global_routing_id.h"
#include "content/test/test_render_frame_host.h"
#include "content/test/test_web_contents.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/test_support/fake_message_dispatch_context.h"
#include "mojo/public/cpp/test_support/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/mojom/compute_pressure/compute_pressure.mojom.h"

namespace content {

class ComputePressureManagerTest : public RenderViewHostImplTestHarness {
 public:
  void SetUp() override {
    RenderViewHostImplTestHarness::SetUp();
    NavigateAndCommit(kTestUrl);

    SetManager(ComputePressureManager::CreateForTesting(
        std::make_unique<FakeCpuProbe>(), base::Milliseconds(1),
        base::Milliseconds(1)));
  }

  void TearDown() override {
    // Get the CpuProbe freed.
    manager_.reset();
    task_environment()->RunUntilIdle();

    RenderViewHostImplTestHarness::TearDown();
  }

  void SetManager(std::unique_ptr<ComputePressureManager> new_manager) {
    manager_ = std::move(new_manager);
    int process_id = main_rfh()->GetProcess()->GetID();
    int routing_id = main_rfh()->GetRoutingID();
    main_frame_id_ = GlobalRenderFrameHostId(process_id, routing_id);

    main_host_.reset();
    manager_->BindReceiver(kTestOrigin, main_frame_id_,
                           main_host_.BindNewPipeAndPassReceiver());
    main_host_sync_ =
        std::make_unique<ComputePressureHostSync>(main_host_.get());
  }

 protected:
  const GURL kTestUrl{"https://example.com/compute_pressure.html"};
  const url::Origin kTestOrigin =
      url::Origin::Create(GURL("https://example.com"));
  const url::Origin kInsecureOrigin =
      url::Origin::Create(GURL("http://example.com"));
  const url::Origin kThirdPartyOrigin =
      url::Origin::Create(GURL("https://other.com"));

  // Quantization scheme used in most tests.
  const blink::mojom::ComputePressureQuantization kQuantization = {
      {0.2, 0.5, 0.8},
      {0.5}};

  base::test::ScopedFeatureList scoped_feature_list_;

  GlobalRenderFrameHostId main_frame_id_;
  // This member is a std::unique_ptr instead of a plain ComputePressureManager
  // so it can be replaced inside tests.
  std::unique_ptr<ComputePressureManager> manager_;
  mojo::Remote<blink::mojom::ComputePressureHost> main_host_;
  std::unique_ptr<ComputePressureHostSync> main_host_sync_;
};

// Disabled on Fuchsia arm64 debug builds: https://crbug.com/1250654
#if defined(OS_FUCHSIA) && defined(_DEBUG) && defined(ARCH_CPU_ARM64)
#define MAYBE_OneObserver DISABLED_OneObserver
#elif defined(OS_LINUX) && defined(USE_OZONE)  // https://crbug.com/1226086
#define MAYBE_OneObserver DISABLED_OneObserver
#else
#define MAYBE_OneObserver OneObserver
#endif
TEST_F(ComputePressureManagerTest, MAYBE_OneObserver) {
  FakeComputePressureObserver observer;
  ASSERT_EQ(main_host_sync_->AddObserver(kQuantization,
                                         observer.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kOk);

  observer.WaitForUpdate();
  EXPECT_THAT(observer.updates(), testing::SizeIs(testing::Ge(1u)));
  EXPECT_THAT(
      observer.updates(),
      testing::Contains(blink::mojom::ComputePressureState(0.35, 0.75)));
}

// Disabled on Fuchsia arm64 debug builds: https://crbug.com/1250654
#if defined(OS_FUCHSIA) && defined(_DEBUG) && defined(ARCH_CPU_ARM64)
#define MAYBE_ThreeObservers DISABLED_ThreeObservers
#elif defined(OS_LINUX) && defined(USE_OZONE)  // https://crbug.com/1226086
#define MAYBE_ThreeObservers DISABLED_ThreeObservers
#else
#define MAYBE_ThreeObservers ThreeObservers
#endif
TEST_F(ComputePressureManagerTest, MAYBE_ThreeObservers) {
  FakeComputePressureObserver observer1;
  ASSERT_EQ(main_host_sync_->AddObserver(kQuantization,
                                         observer1.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kOk);
  FakeComputePressureObserver observer2;
  ASSERT_EQ(main_host_sync_->AddObserver(kQuantization,
                                         observer2.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kOk);
  FakeComputePressureObserver observer3;
  ASSERT_EQ(main_host_sync_->AddObserver(kQuantization,
                                         observer3.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kOk);

  FakeComputePressureObserver::WaitForUpdates(
      {&observer1, &observer2, &observer3});

  EXPECT_THAT(observer1.updates(), testing::SizeIs(testing::Ge(1u)));
  EXPECT_THAT(
      observer1.updates(),
      testing::Contains(blink::mojom::ComputePressureState(0.35, 0.75)));
  EXPECT_THAT(observer2.updates(), testing::SizeIs(testing::Ge(1u)));
  EXPECT_THAT(
      observer2.updates(),
      testing::Contains(blink::mojom::ComputePressureState(0.35, 0.75)));
  EXPECT_THAT(observer3.updates(), testing::SizeIs(testing::Ge(1u)));
  EXPECT_THAT(
      observer3.updates(),
      testing::Contains(blink::mojom::ComputePressureState(0.35, 0.75)));
}

TEST_F(ComputePressureManagerTest, AddObserver_ThirdPartyFrame) {
  mojo::Remote<blink::mojom::ComputePressureHost> third_party_host;
  manager_->BindReceiver(kThirdPartyOrigin, main_frame_id_,
                         third_party_host.BindNewPipeAndPassReceiver());
  auto third_party_host_sync =
      std::make_unique<ComputePressureHostSync>(third_party_host.get());

  FakeComputePressureObserver third_party_observer;
  EXPECT_EQ(third_party_host_sync->AddObserver(
                kQuantization, third_party_observer.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kSecurityError);
  FakeComputePressureObserver first_party_observer;
  EXPECT_EQ(main_host_sync_->AddObserver(
                kQuantization, first_party_observer.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kOk);
}

TEST_F(ComputePressureManagerTest, AddObserver_NoVisibility) {
  test_rvh()->SimulateWasHidden();

  FakeComputePressureObserver observer;
  EXPECT_EQ(main_host_sync_->AddObserver(kQuantization,
                                         observer.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kOk);
}

TEST_F(ComputePressureManagerTest, AddObserver_InvalidFrame) {
  GlobalRenderFrameHostId invalid_routing_id(
      main_rfh()->GetProcess()->GetID() + 1, main_rfh()->GetRoutingID() + 1);
  mojo::Remote<blink::mojom::ComputePressureHost> invalid_host;
  manager_->BindReceiver(kTestOrigin, invalid_routing_id,
                         invalid_host.BindNewPipeAndPassReceiver());
  auto invalid_host_sync =
      std::make_unique<ComputePressureHostSync>(invalid_host.get());
  FakeComputePressureObserver invalid_observer;
  EXPECT_EQ(invalid_host_sync->AddObserver(
                kQuantization, invalid_observer.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kSecurityError);
  FakeComputePressureObserver valid_observer;
  EXPECT_EQ(main_host_sync_->AddObserver(
                kQuantization, valid_observer.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kOk);
}

TEST_F(ComputePressureManagerTest, AddObserver_InvalidQuantization) {
  FakeComputePressureObserver invalid_observer;
  blink::mojom::ComputePressureQuantization invalid_quantization(
      std::vector<double>{-1.0}, std::vector<double>{0.5});

  {
    mojo::test::BadMessageObserver bad_message_observer;
    EXPECT_EQ(
        main_host_sync_->AddObserver(
            invalid_quantization, invalid_observer.BindNewPipeAndPassRemote()),
        blink::mojom::ComputePressureStatus::kSecurityError);
    EXPECT_EQ("Invalid quantization", bad_message_observer.WaitForBadMessage());
  }

  FakeComputePressureObserver valid_observer;
  EXPECT_EQ(main_host_sync_->AddObserver(
                kQuantization, valid_observer.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kOk);
}

TEST_F(ComputePressureManagerTest, AddObserver_InsecureOrigin) {
  {
    mojo::FakeMessageDispatchContext fake_dispatch_context;
    mojo::test::BadMessageObserver bad_message_observer;
    mojo::Remote<blink::mojom::ComputePressureHost> insecure_host;
    manager_->BindReceiver(kInsecureOrigin, main_frame_id_,
                           insecure_host.BindNewPipeAndPassReceiver());
    EXPECT_EQ("Compute Pressure access from an insecure origin",
              bad_message_observer.WaitForBadMessage());
  }

  FakeComputePressureObserver valid_observer;
  EXPECT_EQ(main_host_sync_->AddObserver(
                kQuantization, valid_observer.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kOk);
}

TEST_F(ComputePressureManagerTest, AddObserver_NoProbe) {
  SetManager(ComputePressureManager::CreateForTesting(
      /*cpu_probe=*/nullptr, base::Milliseconds(1), base::Milliseconds(1)));

  FakeComputePressureObserver observer;
  EXPECT_EQ(main_host_sync_->AddObserver(kQuantization,
                                         observer.BindNewPipeAndPassRemote()),
            blink::mojom::ComputePressureStatus::kNotSupported);
}

TEST_F(ComputePressureManagerTest, AddObserver_NoFeature) {
  scoped_feature_list_.Reset();
  scoped_feature_list_.InitAndDisableFeature(blink::features::kComputePressure);

  mojo::FakeMessageDispatchContext fake_dispatch_context;
  mojo::test::BadMessageObserver bad_message_observer;
  mojo::Remote<blink::mojom::ComputePressureHost> insecure_host;
  manager_->BindReceiver(kInsecureOrigin, main_frame_id_,
                         insecure_host.BindNewPipeAndPassReceiver());
  EXPECT_EQ("Compute Pressure not enabled",
            bad_message_observer.WaitForBadMessage());
}

}  // namespace content
