blob: dd5c86716b1ce4a8b0c0cd1d0fde1fe6fdfbc5a5 [file] [log] [blame]
// Copyright 2016 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 "base/bind.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "build/build_config.h"
#include "content/browser/utility_process_host.h"
#include "content/public/browser/browser_child_process_observer.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_data.h"
#include "content/public/browser/child_process_termination_info.h"
#include "content/public/common/bind_interface_helpers.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_service.mojom.h"
#if defined(OS_MACOSX) || defined(OS_LINUX)
#include <sys/wait.h>
#endif
#if defined(OS_WIN)
#include <windows.h>
#endif // OS_WIN
namespace content {
namespace {
const char kTestProcessName[] = "test_process";
} // namespace
class UtilityProcessHostBrowserTest : public BrowserChildProcessObserver,
public ContentBrowserTest {
public:
void RunUtilityProcess(bool elevated, bool crash) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BrowserChildProcessObserver::Add(this);
has_crashed = false;
base::RunLoop run_loop;
done_closure_ =
base::BindOnce(&UtilityProcessHostBrowserTest::DoneRunning,
base::Unretained(this), run_loop.QuitClosure(), crash);
base::PostTask(
FROM_HERE, {BrowserThread::IO},
base::BindOnce(
&UtilityProcessHostBrowserTest::RunUtilityProcessOnIOThread,
base::Unretained(this), elevated, crash));
run_loop.Run();
}
protected:
void DoneRunning(base::OnceClosure quit_closure, bool expect_crashed) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BrowserChildProcessObserver::Remove(this);
EXPECT_EQ(expect_crashed, has_crashed);
std::move(quit_closure).Run();
}
void RunUtilityProcessOnIOThread(bool elevated, bool crash) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
UtilityProcessHost* host = new UtilityProcessHost();
host->SetName(base::ASCIIToUTF16("TestProcess"));
host->SetMetricsName(kTestProcessName);
#if defined(OS_WIN)
if (elevated)
host->SetSandboxType(service_manager::SandboxType::
SANDBOX_TYPE_NO_SANDBOX_AND_ELEVATED_PRIVILEGES);
#endif
EXPECT_TRUE(host->Start());
BindInterface(host, &service_);
if (crash) {
service_->DoCrashImmediately(
base::BindOnce(&UtilityProcessHostBrowserTest::OnSomethingOnIOThread,
base::Unretained(this), crash));
} else {
service_->DoSomething(
base::BindOnce(&UtilityProcessHostBrowserTest::OnSomethingOnIOThread,
base::Unretained(this), crash));
}
}
void ResetServiceOnIOThread() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
service_.reset();
}
void OnSomethingOnIOThread(bool expect_crash) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
// If service crashes then this never gets called.
ASSERT_EQ(false, expect_crash);
ResetServiceOnIOThread();
base::PostTask(FROM_HERE, {BrowserThread::UI}, std::move(done_closure_));
}
mojom::TestServicePtr service_;
base::OnceClosure done_closure_;
// Access on UI thread.
bool has_crashed;
private:
// content::BrowserChildProcessObserver implementation:
void BrowserChildProcessKilled(
const ChildProcessData& data,
const ChildProcessTerminationInfo& info) override {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
#if defined(OS_ANDROID)
// Android does not send crash notifications but sends kills. See comment in
// browser_child_process_observer.h.
BrowserChildProcessCrashed(data, info);
#else
FAIL() << "Killed notifications should only happen on Android.";
#endif
}
void BrowserChildProcessCrashed(
const ChildProcessData& data,
const ChildProcessTerminationInfo& info) override {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
#if defined(OS_WIN)
EXPECT_EQ(EXCEPTION_BREAKPOINT, DWORD{info.exit_code});
#elif defined(OS_MACOSX) || defined(OS_LINUX)
EXPECT_TRUE(WIFSIGNALED(info.exit_code));
EXPECT_EQ(SIGTRAP, WTERMSIG(info.exit_code));
#endif
EXPECT_EQ(kTestProcessName, data.metrics_name);
EXPECT_EQ(false, has_crashed);
has_crashed = true;
base::PostTask(
FROM_HERE, {BrowserThread::IO},
base::BindOnce(&UtilityProcessHostBrowserTest::ResetServiceOnIOThread,
base::Unretained(this)));
std::move(done_closure_).Run();
}
};
IN_PROC_BROWSER_TEST_F(UtilityProcessHostBrowserTest, LaunchProcess) {
RunUtilityProcess(false, false);
}
IN_PROC_BROWSER_TEST_F(UtilityProcessHostBrowserTest, LaunchProcessAndCrash) {
RunUtilityProcess(false, true);
}
#if defined(OS_WIN)
// Times out. crbug.com/927298.
IN_PROC_BROWSER_TEST_F(UtilityProcessHostBrowserTest,
DISABLED_LaunchElevatedProcess) {
RunUtilityProcess(true, false);
}
// Disabled because currently this causes a WER dialog to appear.
IN_PROC_BROWSER_TEST_F(UtilityProcessHostBrowserTest,
LaunchElevatedProcessAndCrash_DISABLED) {
RunUtilityProcess(true, true);
}
#endif
} // namespace content