910e62b5创建于 1月15日历史提交
// Copyright 2025 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/devtools/remote_debugging_server.h"

#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/devtools/features.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/policy/core/common/mock_configuration_policy_provider.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "content/public/browser/devtools_agent_host.h"
#include "content/public/browser/devtools_socket_factory.h"
#include "content/public/common/content_constants.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::_;
using testing::NiceMock;
using testing::Return;

class MockRemoteDebuggingServer : public RemoteDebuggingServer {
 public:
  MOCK_METHOD(void,
              StartHttpServer,
              (std::unique_ptr<content::DevToolsSocketFactory>,
               const base::FilePath&,
               const base::FilePath&,
               content::DevToolsAgentHost::RemoteDebuggingServerMode),
              (override));
  MOCK_METHOD(void, StopHttpServer, (), (override));
  MOCK_METHOD(void, StartPipeHandler, (), (override));
  MOCK_METHOD(void, StopPipeHandler, (), (override));
};

class RemoteDebuggingServerTest : public testing::Test {
 public:
  RemoteDebuggingServerTest()
      : profile_manager_(TestingBrowserProcess::GetGlobal()) {
    feature_list_.InitAndEnableFeature(
        features::kDevToolsAcceptDebuggingConnections);
  }

  void SetUp() override {
    ASSERT_TRUE(profile_manager_.SetUp());
    base::CommandLine::ForCurrentProcess()->InitFromArgv({});
  }

 protected:
  content::BrowserTaskEnvironment task_environment_;
  TestingProfileManager profile_manager_;
  base::test::ScopedFeatureList feature_list_;
};

TEST_F(RemoteDebuggingServerTest, StartsAndStopsWithPref) {
  TestingPrefServiceSimple* local_state =
      TestingBrowserProcess::GetGlobal()->GetTestingLocalState();
  auto server = std::make_unique<NiceMock<MockRemoteDebuggingServer>>();

  EXPECT_CALL(*server, StartHttpServer).Times(0);
  server->StartHttpServerInApprovalMode(local_state);
  testing::Mock::VerifyAndClearExpectations(server.get());

  base::RunLoop start_run_loop;
  EXPECT_CALL(*server, StartHttpServer)
      .WillOnce(testing::WithoutArgs(
          testing::Invoke(&start_run_loop, &base::RunLoop::Quit)));
  local_state->SetUserPref(prefs::kDevToolsRemoteDebuggingEnabled,
                           std::make_unique<base::Value>(true));
  start_run_loop.Run();
  testing::Mock::VerifyAndClearExpectations(server.get());

  base::RunLoop stop_run_loop;
  EXPECT_CALL(*server, StopHttpServer)
      .WillOnce(testing::Invoke(&stop_run_loop, &base::RunLoop::Quit));
  local_state->SetUserPref(prefs::kDevToolsRemoteDebuggingEnabled,
                           std::make_unique<base::Value>(false));
  stop_run_loop.Run();
  testing::Mock::VerifyAndClearExpectations(server.get());
}

TEST_F(RemoteDebuggingServerTest, DoesNotStartWhenFeatureDisabled) {
  base::test::ScopedFeatureList feature_list;
  feature_list.InitAndDisableFeature(
      features::kDevToolsAcceptDebuggingConnections);
  TestingPrefServiceSimple* local_state =
      TestingBrowserProcess::GetGlobal()->GetTestingLocalState();
  auto server = RemoteDebuggingServer::GetInstance(local_state);
  EXPECT_EQ(server.error(),
            RemoteDebuggingServer::NotStartedReason::kNotRequested);
}

TEST_F(RemoteDebuggingServerTest, DoesNotStartWhenDisallowedByPolicy) {
  NiceMock<policy::MockConfigurationPolicyProvider> policy_provider;
  policy_provider.SetDefaultReturns(
      /*is_initialization_complete_return=*/true,
      /*is_first_policy_load_complete_return=*/true);
  policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&policy_provider);
  TestingPrefServiceSimple* local_state =
      TestingBrowserProcess::GetGlobal()->GetTestingLocalState();
  local_state->SetManagedPref(prefs::kDevToolsRemoteDebuggingAllowed,
                              std::make_unique<base::Value>(false));
  auto server = RemoteDebuggingServer::GetInstance(local_state);
  EXPECT_EQ(server.error(),
            RemoteDebuggingServer::NotStartedReason::kDisabledByPolicy);
  policy::BrowserPolicyConnector::SetPolicyProviderForTesting(nullptr);
}

TEST_F(RemoteDebuggingServerTest, GetPortFromUserDataDir) {
  base::ScopedTempDir temp_dir;
  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  base::FilePath active_port_file =
      temp_dir.GetPath().Append(content::kDevToolsActivePortFileName);

  // Test with a valid port file.
  ASSERT_TRUE(base::WriteFile(active_port_file, "12345"));
  EXPECT_EQ(12345,
            RemoteDebuggingServer::GetPortFromUserDataDir(temp_dir.GetPath()));

  // Test with an empty port file.
  ASSERT_TRUE(base::WriteFile(active_port_file, ""));
  EXPECT_EQ(RemoteDebuggingServer::kDefaultDevToolsPort,
            RemoteDebuggingServer::GetPortFromUserDataDir(temp_dir.GetPath()));

  // Test with a malformed port file.
  ASSERT_TRUE(base::WriteFile(active_port_file, "hello"));
  EXPECT_EQ(RemoteDebuggingServer::kDefaultDevToolsPort,
            RemoteDebuggingServer::GetPortFromUserDataDir(temp_dir.GetPath()));

  // Test with a port file that has extra content.
  ASSERT_TRUE(base::WriteFile(active_port_file, "12345\nfoo"));
  EXPECT_EQ(12345,
            RemoteDebuggingServer::GetPortFromUserDataDir(temp_dir.GetPath()));

  // Test with a negative port.
  ASSERT_TRUE(base::WriteFile(active_port_file, "-1"));
  EXPECT_EQ(RemoteDebuggingServer::kDefaultDevToolsPort,
            RemoteDebuggingServer::GetPortFromUserDataDir(temp_dir.GetPath()));

  // Test with a valid port at the upper boundary.
  ASSERT_TRUE(base::WriteFile(active_port_file, "65535"));
  EXPECT_EQ(65535,
            RemoteDebuggingServer::GetPortFromUserDataDir(temp_dir.GetPath()));

  // Test with an out of bounds port.
  ASSERT_TRUE(base::WriteFile(active_port_file, "65536"));
  EXPECT_EQ(RemoteDebuggingServer::kDefaultDevToolsPort,
            RemoteDebuggingServer::GetPortFromUserDataDir(temp_dir.GetPath()));

  // Test with no port file.
  ASSERT_TRUE(base::DeleteFile(active_port_file));
  EXPECT_EQ(RemoteDebuggingServer::kDefaultDevToolsPort,
            RemoteDebuggingServer::GetPortFromUserDataDir(temp_dir.GetPath()));
}