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

#include "chrome/browser/extensions/event_router_forwarder.h"

#include <utility>

#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/test/thread_test_helper.h"
#include "build/build_config.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/browser_task_environment.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/common/extension_id.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));

namespace extensions {

namespace {

const events::HistogramValue kHistogramValue = events::FOR_TEST;
const char kEventName[] = "event_name";

class MockEventRouterForwarder : public EventRouterForwarder {
 public:
  MOCK_METHOD3(CallEventRouter,
               void(Profile*, events::HistogramValue, const std::string&));

  void CallEventRouter(Profile* profile,
                       events::HistogramValue histogram_value,
                       const std::string& event_name,
                       base::ListValue args) override {
    CallEventRouter(profile, histogram_value, event_name);
  }

 protected:
  ~MockEventRouterForwarder() override = default;
};

static void BroadcastEventToRenderers(
    EventRouterForwarder* event_router,
    events::HistogramValue histogram_value,
    const std::string& event_name,
    bool dispatch_to_off_the_record_profiles) {
  event_router->BroadcastEventToRenderers(histogram_value, event_name,
                                          base::ListValue(),
                                          dispatch_to_off_the_record_profiles);
}

class EventRouterForwarderTest : public testing::Test {
 protected:
  EventRouterForwarderTest()
      : task_environment_(content::BrowserTaskEnvironment::REAL_IO_THREAD),
        profile_manager_(TestingBrowserProcess::GetGlobal()) {
  }

  void SetUp() override {
    ASSERT_TRUE(profile_manager_.SetUp());

    // Inject a BrowserProcess with a ProfileManager.
    profile1_ = profile_manager_.CreateTestingProfile("one");
    profile2_ = profile_manager_.CreateTestingProfile("two");
  }

  content::BrowserTaskEnvironment task_environment_;
  TestingProfileManager profile_manager_;
  // Profiles are weak pointers, owned by ProfileManager in |browser_process_|.
  raw_ptr<TestingProfile> profile1_;
  raw_ptr<TestingProfile> profile2_;
};

TEST_F(EventRouterForwarderTest, BroadcastRendererUI) {
  scoped_refptr<MockEventRouterForwarder> event_router =
      base::MakeRefCounted<MockEventRouterForwarder>();
  GURL url;
  EXPECT_CALL(*event_router,
              CallEventRouter(profile1_.get(), kHistogramValue, kEventName));
  EXPECT_CALL(*event_router,
              CallEventRouter(profile2_.get(), kHistogramValue, kEventName));
  BroadcastEventToRenderers(event_router.get(), kHistogramValue, kEventName,
                            false);
}

TEST_F(EventRouterForwarderTest, BroadcastRendererUIIncognito) {
  scoped_refptr<MockEventRouterForwarder> event_router =
      base::MakeRefCounted<MockEventRouterForwarder>();
  using ::testing::_;
  GURL url;
  Profile* incognito =
      profile1_->GetPrimaryOTRProfile(/*create_if_needed=*/true);
  EXPECT_CALL(*event_router,
              CallEventRouter(profile1_.get(), kHistogramValue, kEventName));
  EXPECT_CALL(*event_router, CallEventRouter(incognito, _, _)).Times(0);
  EXPECT_CALL(*event_router,
              CallEventRouter(profile2_.get(), kHistogramValue, kEventName));
  BroadcastEventToRenderers(event_router.get(), kHistogramValue, kEventName,
                            false);
}

TEST_F(EventRouterForwarderTest,
       BroadcastRendererUIIncognitoWithDispatchToOffTheRecordProfiles) {
  scoped_refptr<MockEventRouterForwarder> event_router =
      base::MakeRefCounted<MockEventRouterForwarder>();
  using ::testing::_;
  GURL url;
  Profile* incognito1 =
      profile1_->GetPrimaryOTRProfile(/*create_if_needed=*/true);
  Profile* incognito2 =
      profile2_->GetPrimaryOTRProfile(/*create_if_needed=*/true);
  EXPECT_CALL(*event_router,
              CallEventRouter(profile1_.get(), kHistogramValue, kEventName));
  EXPECT_CALL(*event_router, CallEventRouter(incognito1, _, _));
  EXPECT_CALL(*event_router,
              CallEventRouter(profile2_.get(), kHistogramValue, kEventName));
  EXPECT_CALL(*event_router, CallEventRouter(incognito2, _, _));
  BroadcastEventToRenderers(event_router.get(), kHistogramValue, kEventName,
                            true);
}

// This is the canonical test for passing control flow from the IO thread
// to the UI thread. Repeating this for all public functions of
// EventRouterForwarder would not increase coverage.
TEST_F(EventRouterForwarderTest, BroadcastRendererIO) {
  scoped_refptr<MockEventRouterForwarder> event_router =
      base::MakeRefCounted<MockEventRouterForwarder>();
  GURL url;
  EXPECT_CALL(*event_router,
              CallEventRouter(profile1_.get(), kHistogramValue, kEventName));
  EXPECT_CALL(*event_router,
              CallEventRouter(profile2_.get(), kHistogramValue, kEventName));
  content::GetIOThreadTaskRunner({})->PostTask(
      FROM_HERE, base::BindOnce(&BroadcastEventToRenderers,
                                base::Unretained(event_router.get()),
                                kHistogramValue, kEventName, false));

  // Wait for IO thread's message loop to be processed
  scoped_refptr<base::ThreadTestHelper> helper(
      new base::ThreadTestHelper(content::GetIOThreadTaskRunner({}).get()));
  ASSERT_TRUE(helper->Run());

  base::RunLoop().RunUntilIdle();
}

}  // namespace
}  // namespace extensions
