1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <stdio.h>
6#include <string>
7
8#include "base/compiler_specific.h"
9#include "base/environment.h"
10#include "base/logging.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/strings/stringprintf.h"
13#include "base/strings/utf_string_conversions.h"
14#include "breakpad/src/client/windows/crash_generation/client_info.h"
15#include "breakpad/src/client/windows/crash_generation/crash_generation_server.h"
16#include "testing/gmock/include/gmock/gmock.h"
17#include "testing/gtest/include/gtest/gtest.h"
18
19namespace {
20
21// The name of the environment variable used to pass the crash server pipe name
22// to the crashing child process.
23const char kPipeVariableName[] = "REMOTING_BREAKPAD_WIN_DEATH_TEST_PIPE_NAME";
24
25// The prefix string used to generate a unique crash server pipe name.
26// The name has to be unique as multiple test instances can be running
27// simultaneously.
28const wchar_t kPipeNamePrefix[] = L"\\\\.\\pipe\\";
29
30class MockCrashServerCallbacks {
31 public:
32  MockCrashServerCallbacks();
33  virtual ~MockCrashServerCallbacks();
34
35  // |google_breakpad::CrashGenerationServer| invokes callbacks from artitrary
36  // thread pool threads. |OnClientDumpRequested| is the only one that happened
37  // to be called in synchronous manner. While it is still called on
38  // a thread pool thread, the crashing process will wait until the server
39  // signals an event after |OnClientDumpRequested| completes (or until 15
40  // seconds timeout expires).
41  MOCK_METHOD0(OnClientDumpRequested, void());
42
43  static void OnClientDumpRequestCallback(
44      void* context,
45      const google_breakpad::ClientInfo* client_info,
46      const std::wstring* file_path);
47};
48
49MockCrashServerCallbacks::MockCrashServerCallbacks() {
50}
51
52MockCrashServerCallbacks::~MockCrashServerCallbacks() {
53}
54
55// static
56void MockCrashServerCallbacks::OnClientDumpRequestCallback(
57    void* context,
58    const google_breakpad::ClientInfo* /* client_info */,
59    const std::wstring* /* file_path */) {
60  reinterpret_cast<MockCrashServerCallbacks*>(context)->OnClientDumpRequested();
61}
62
63}  // namespace
64
65namespace remoting {
66
67void InitializeCrashReportingForTest(const wchar_t* pipe_name);
68
69class BreakpadWinDeathTest : public testing::Test {
70 public:
71  BreakpadWinDeathTest();
72  virtual ~BreakpadWinDeathTest();
73
74  virtual void SetUp() OVERRIDE;
75
76 protected:
77  scoped_ptr<google_breakpad::CrashGenerationServer> crash_server_;
78  scoped_ptr<MockCrashServerCallbacks> callbacks_;
79  std::wstring pipe_name_;
80};
81
82BreakpadWinDeathTest::BreakpadWinDeathTest() {
83}
84
85BreakpadWinDeathTest::~BreakpadWinDeathTest() {
86}
87
88void BreakpadWinDeathTest::SetUp() {
89  scoped_ptr<base::Environment> environment(base::Environment::Create());
90  std::string pipe_name;
91  if (environment->GetVar(kPipeVariableName, &pipe_name)) {
92    // This is a child process. Initialize crash dump reporting to the crash
93    // dump server.
94    pipe_name_ = base::UTF8ToWide(pipe_name);
95    InitializeCrashReportingForTest(pipe_name_.c_str());
96  } else {
97    // This is the parent process. Generate a unique pipe name and setup
98    // a dummy crash dump server.
99    UUID guid = {0};
100    RPC_STATUS status = UuidCreate(&guid);
101    EXPECT_TRUE(status == RPC_S_OK || status == RPC_S_UUID_LOCAL_ONLY);
102
103    pipe_name_ =
104        base::StringPrintf(
105            L"%ls%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
106            kPipeNamePrefix,
107            guid.Data1,
108            guid.Data2,
109            guid.Data3,
110            guid.Data4[0],
111            guid.Data4[1],
112            guid.Data4[2],
113            guid.Data4[3],
114            guid.Data4[4],
115            guid.Data4[5],
116            guid.Data4[6],
117            guid.Data4[7]);
118    EXPECT_TRUE(environment->SetVar(kPipeVariableName,
119                                    base::WideToUTF8(pipe_name_)));
120
121    // Setup a dummy crash dump server.
122    callbacks_.reset(new MockCrashServerCallbacks());
123    crash_server_.reset(
124        new google_breakpad::CrashGenerationServer(
125            pipe_name_,
126            NULL,
127            NULL,
128            NULL,
129            MockCrashServerCallbacks::OnClientDumpRequestCallback,
130            callbacks_.get(),
131            NULL,
132            NULL,
133            NULL,
134            NULL,
135            false,
136            NULL));
137    ASSERT_TRUE(crash_server_->Start());
138  }
139}
140
141TEST_F(BreakpadWinDeathTest, TestAccessViolation) {
142  if (callbacks_.get()) {
143    EXPECT_CALL(*callbacks_, OnClientDumpRequested());
144  }
145
146  // Generate access violation exception.
147  ASSERT_DEATH(*reinterpret_cast<int*>(NULL) = 1, "");
148}
149
150TEST_F(BreakpadWinDeathTest, TestInvalidParameter) {
151  if (callbacks_.get()) {
152    EXPECT_CALL(*callbacks_, OnClientDumpRequested());
153  }
154
155  // Cause the invalid parameter callback to be called.
156  ASSERT_EXIT(printf(NULL), testing::ExitedWithCode(0), "");
157}
158
159TEST_F(BreakpadWinDeathTest, TestDebugbreak) {
160  if (callbacks_.get()) {
161    EXPECT_CALL(*callbacks_, OnClientDumpRequested());
162  }
163
164  // See if __debugbreak() is intercepted.
165  ASSERT_DEATH(__debugbreak(), "");
166}
167
168}  // namespace remoting
169