chromium/content/browser/browser_child_process_observer_browsertest.cc

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

#include "content/public/browser/browser_child_process_observer.h"

#include "base/functional/bind.h"
#include "base/run_loop.h"
#include "build/build_config.h"
#include "content/browser/browser_child_process_host_impl.h"
#include "content/browser/child_process_host_impl.h"
#include "content/browser/utility_process_host.h"
#include "content/public/browser/browser_child_process_host.h"
#include "content/public/browser/browser_child_process_host_delegate.h"
#include "content/public/browser/child_process_data.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/process_type.h"
#include "content/public/common/sandboxed_process_launcher_delegate.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/test_service.mojom.h"
#include "sandbox/policy/sandbox_type.h"
#include "testing/gmock/include/gmock/gmock.h"

namespace content {

namespace {

// An enum that represent the different type of notitifcations that exist in
// BrowserChildProcessObserver.
enum class Notification {};

// Nicer test output.
std::ostream& operator<<(std::ostream& os, Notification notification) {}

// Returns true if a child process whose ID is |child_id| is still alive.
bool IsHostAlive(int child_id) {}

}  // namespace

// A test BrowserChildProcessObserver that transforms every call to one of the
// observer's method to a call to the notification callback.
class BrowserChildProcessNotificationObserver
    : public BrowserChildProcessObserver {};

// A helper class that allows the user to wait until a specific |notification|
// is sent for a child process whose ID matches |child_id|.
class WaitForNotificationObserver {};

class TestSandboxedProcessLauncherDelegate
    : public SandboxedProcessLauncherDelegate {};

// A test-specific type of process host. Self-owned.
class TestProcessHost : public BrowserChildProcessHostDelegate {};

// A helper class that exposes which notifications were sent for a specific
// child process.
class TestBrowserChildProcessObserver {};

class BrowserChildProcessObserverBrowserTest : public ContentBrowserTest {};

// Tests that launching and then using ForceShutdown() results in a normal
// termination.
#if defined(ADDRESS_SANITIZER)
// TODO(crbug.com/40238612): Fix ASAN failures on trybot.
#define MAYBE_LaunchAndForceShutdown
#else
#define MAYBE_LaunchAndForceShutdown
#endif
IN_PROC_BROWSER_TEST_F(BrowserChildProcessObserverBrowserTest,
                       MAYBE_LaunchAndForceShutdown) {}

// Tests that launching and then deleting the host results in a normal
// termination.
IN_PROC_BROWSER_TEST_F(BrowserChildProcessObserverBrowserTest,
                       LaunchAndDelete) {}

// Tests that launching and then disconnecting the service channel results in a
// normal termination.
// Note: This only works for services bound using BindServiceInterface(), not
// BindReceiver().
#if defined(ADDRESS_SANITIZER)
// TODO(crbug.com/40238612): Fix ASAN failures on trybot.
#define MAYBE_LaunchAndDisconnect
#else
#define MAYBE_LaunchAndDisconnect
#endif
IN_PROC_BROWSER_TEST_F(BrowserChildProcessObserverBrowserTest,
                       MAYBE_LaunchAndDisconnect) {}

// Tests that launching and then causing a crash the host results in a crashed
// notification.
// TODO(crbug.com/40868150): Times out on Android tests.
#if BUILDFLAG(IS_ANDROID)
#define MAYBE_LaunchAndCrash
#else
#define MAYBE_LaunchAndCrash
#endif
IN_PROC_BROWSER_TEST_F(BrowserChildProcessObserverBrowserTest,
                       MAYBE_LaunchAndCrash) {}

// Tests that kLaunchFailed is correctly sent when the child process fails to
// launch.
//
// This test won't work as-is on POSIX platforms, where fork()+exec() is used to
// launch child processes, failure does not happen until exec(), therefore the
// test will see a valid child process followed by a
// TERMINATION_STATUS_ABNORMAL_TERMINATION of the forked process. However,
// posix_spawn() is used on macOS.
// See also ServiceProcessLauncherTest.FailToLaunchProcess.
#if !BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_F(BrowserChildProcessObserverBrowserTest, LaunchFailed) {
  base::WeakPtr<TestProcessHost> host = TestProcessHost::Create();
  int child_id = host->GetId();

#if BUILDFLAG(IS_WIN)
  // The Windows sandbox does not like the child process being a different
  // process, so launch unsandboxed for the purpose of this test.
  host->SetSandboxType(sandbox::mojom::Sandbox::kNoSandbox);
#endif

  // Simulate a catastrophic launch failure for all child processes by
  // making the path to the process non-existent.
  base::CommandLine::ForCurrentProcess()->AppendSwitchPath(
      switches::kBrowserSubprocessPath,
      base::FilePath(FILE_PATH_LITERAL("non_existent_path")));

  TestBrowserChildProcessObserver observer(child_id);

  {
    WaitForNotificationObserver waiter(child_id, Notification::kLaunchFailed);
    host->LaunchProcess();
    waiter.Wait();
  }

  // The host should be deleted now.
  EXPECT_FALSE(host);
  EXPECT_FALSE(IsHostAlive(child_id));
  EXPECT_THAT(observer.notifications(),
              testing::ElementsAreArray({Notification::kLaunchFailed}));
}
#endif  // !BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_MAC)

}  // namespace content