PageRenderTime 38ms CodeModel.GetById 10ms app.highlight 25ms RepoModel.GetById 1ms app.codeStats 0ms

/thirdparty/breakpad/client/windows/unittests/crash_generation_server_test.cc

http://github.com/tomahawk-player/tomahawk
C++ | 306 lines | 208 code | 58 blank | 40 comment | 13 complexity | e7c45502248e35d16f9ee2b86c446570 MD5 | raw file
  1// Copyright 2010, Google Inc.
  2// All rights reserved.
  3//
  4// Redistribution and use in source and binary forms, with or without
  5// modification, are permitted provided that the following conditions are
  6// met:
  7//
  8//     * Redistributions of source code must retain the above copyright
  9// notice, this list of conditions and the following disclaimer.
 10//     * Redistributions in binary form must reproduce the above
 11// copyright notice, this list of conditions and the following disclaimer
 12// in the documentation and/or other materials provided with the
 13// distribution.
 14//     * Neither the name of Google Inc. nor the names of its
 15// contributors may be used to endorse or promote products derived from
 16// this software without specific prior written permission.
 17//
 18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 29
 30
 31#include "testing/gtest/include/gtest/gtest.h"
 32#include "testing/include/gmock/gmock.h"
 33
 34#include "client/windows/crash_generation/crash_generation_server.h"
 35#include "client/windows/common/ipc_protocol.h"
 36
 37using testing::_;
 38
 39namespace {
 40
 41const wchar_t kPipeName[] =
 42  L"\\\\.\\pipe\\CrashGenerationServerTest\\TestCaseServer";
 43
 44const DWORD kPipeDesiredAccess = FILE_READ_DATA |
 45                                 FILE_WRITE_DATA |
 46                                 FILE_WRITE_ATTRIBUTES;
 47
 48const DWORD kPipeFlagsAndAttributes = SECURITY_IDENTIFICATION |
 49                                      SECURITY_SQOS_PRESENT;
 50
 51const DWORD kPipeMode = PIPE_READMODE_MESSAGE;
 52
 53int kCustomInfoCount = 2;
 54
 55google_breakpad::CustomInfoEntry kCustomInfoEntries[] = {
 56    google_breakpad::CustomInfoEntry(L"prod", L"CrashGenerationServerTest"),
 57    google_breakpad::CustomInfoEntry(L"ver", L"1.0"),
 58};
 59
 60class CrashGenerationServerTest : public ::testing::Test {
 61 public:
 62  CrashGenerationServerTest()
 63      : crash_generation_server_(kPipeName,
 64                                 NULL,
 65                                 CallOnClientConnected, &mock_callbacks_,
 66                                 CallOnClientDumpRequested, &mock_callbacks_,
 67                                 CallOnClientExited, &mock_callbacks_,
 68                                 CallOnClientUploadRequested, &mock_callbacks_,
 69                                 false,
 70                                 NULL),
 71        thread_id_(0),
 72        exception_pointers_(NULL) {
 73    memset(&assert_info_, 0, sizeof(assert_info_));
 74  }
 75
 76 protected:
 77  class MockCrashGenerationServerCallbacks {
 78   public:
 79    MOCK_METHOD1(OnClientConnected,
 80                 void(const google_breakpad::ClientInfo* client_info));
 81    MOCK_METHOD2(OnClientDumpRequested,
 82                 void(const google_breakpad::ClientInfo* client_info,
 83                      const std::wstring* file_path));
 84    MOCK_METHOD1(OnClientExited,
 85                 void(const google_breakpad::ClientInfo* client_info));
 86    MOCK_METHOD1(OnClientUploadRequested,
 87                 void(const DWORD crash_id));
 88  };
 89
 90  enum ClientFault {
 91    NO_FAULT,
 92    CLOSE_AFTER_CONNECT,
 93    SEND_INVALID_REGISTRATION,
 94    TRUNCATE_REGISTRATION,
 95    CLOSE_AFTER_REGISTRATION,
 96    RESPONSE_BUFFER_TOO_SMALL,
 97    CLOSE_AFTER_RESPONSE,
 98    SEND_INVALID_ACK
 99  };
100
101  void SetUp() {
102    ASSERT_TRUE(crash_generation_server_.Start());
103  }
104
105  void FaultyClient(ClientFault fault_type) {
106    HANDLE pipe = CreateFile(kPipeName,
107                             kPipeDesiredAccess,
108                             0,
109                             NULL,
110                             OPEN_EXISTING,
111                             kPipeFlagsAndAttributes,
112                             NULL);
113
114    if (pipe == INVALID_HANDLE_VALUE) {
115      ASSERT_EQ(ERROR_PIPE_BUSY, GetLastError());
116
117      // Cannot continue retrying if wait on pipe fails.
118      ASSERT_TRUE(WaitNamedPipe(kPipeName, 500));
119
120      pipe = CreateFile(kPipeName,
121                        kPipeDesiredAccess,
122                        0,
123                        NULL,
124                        OPEN_EXISTING,
125                        kPipeFlagsAndAttributes,
126                        NULL);
127    }
128
129    ASSERT_NE(pipe, INVALID_HANDLE_VALUE);
130
131    DWORD mode = kPipeMode;
132    ASSERT_TRUE(SetNamedPipeHandleState(pipe, &mode, NULL, NULL));
133
134    DoFaultyClient(fault_type, pipe);
135
136    CloseHandle(pipe);
137  }
138
139  void DoTestFault(ClientFault fault) {
140    EXPECT_CALL(mock_callbacks_, OnClientConnected(_)).Times(0);
141    ASSERT_NO_FATAL_FAILURE(FaultyClient(fault));
142    ASSERT_NO_FATAL_FAILURE(FaultyClient(fault));
143    ASSERT_NO_FATAL_FAILURE(FaultyClient(fault));
144
145    EXPECT_CALL(mock_callbacks_, OnClientConnected(_));
146
147    ASSERT_NO_FATAL_FAILURE(FaultyClient(NO_FAULT));
148
149    // Slight hack. The OnClientConnected is only invoked after the ack is
150    // received by the server. At that point, the FaultyClient call has already
151    // returned. The best way to wait until the server is done handling that is
152    // to send one more ping, whose processing will be blocked by delivery of
153    // the OnClientConnected message.
154    ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT));
155  }
156
157  MockCrashGenerationServerCallbacks mock_callbacks_;
158
159 private:
160  // Depends on the caller to successfully open the pipe before invocation and
161  // to close it immediately afterwards.
162  void DoFaultyClient(ClientFault fault_type, HANDLE pipe) {
163    if (fault_type == CLOSE_AFTER_CONNECT) {
164      return;
165    }
166
167    google_breakpad::CustomClientInfo custom_info = {kCustomInfoEntries,
168                                                     kCustomInfoCount};
169
170    google_breakpad::ProtocolMessage msg(
171      fault_type == SEND_INVALID_REGISTRATION ?
172        google_breakpad::MESSAGE_TAG_NONE :
173        google_breakpad::MESSAGE_TAG_REGISTRATION_REQUEST,
174      GetCurrentProcessId(),
175      MiniDumpNormal,
176      &thread_id_,
177      &exception_pointers_,
178      &assert_info_,
179      custom_info,
180      NULL,
181      NULL,
182      NULL);
183
184    DWORD bytes_count = 0;
185
186    ASSERT_TRUE(WriteFile(pipe,
187                          &msg,
188                          fault_type == TRUNCATE_REGISTRATION ?
189                            sizeof(msg) / 2 : sizeof(msg),
190                          &bytes_count,
191                          NULL));
192
193    if (fault_type == CLOSE_AFTER_REGISTRATION) {
194      return;
195    }
196
197    google_breakpad::ProtocolMessage reply;
198
199    if (!ReadFile(pipe,
200                  &reply,
201                  fault_type == RESPONSE_BUFFER_TOO_SMALL ?
202                    sizeof(google_breakpad::ProtocolMessage) / 2 :
203                    sizeof(google_breakpad::ProtocolMessage),
204                  &bytes_count,
205                  NULL)) {
206      switch (fault_type) {
207        case TRUNCATE_REGISTRATION:
208        case RESPONSE_BUFFER_TOO_SMALL:
209        case SEND_INVALID_REGISTRATION:
210          return;
211
212        default:
213          FAIL() << "Unexpectedly failed to register.";
214      }
215    }
216
217    if (fault_type == CLOSE_AFTER_RESPONSE) {
218      return;
219    }
220
221    google_breakpad::ProtocolMessage ack_msg;
222    ack_msg.tag = google_breakpad::MESSAGE_TAG_REGISTRATION_ACK;
223
224    ASSERT_TRUE(WriteFile(pipe,
225                          &ack_msg,
226                          SEND_INVALID_ACK ?
227                            sizeof(ack_msg) : sizeof(ack_msg) / 2,
228                          &bytes_count,
229                          NULL));
230
231    return;
232  }
233
234  static void CallOnClientConnected(
235    void* context, const google_breakpad::ClientInfo* client_info) {
236    static_cast<MockCrashGenerationServerCallbacks*>(context)->
237      OnClientConnected(client_info);
238  }
239
240  static void CallOnClientDumpRequested(
241    void* context,
242    const google_breakpad::ClientInfo* client_info,
243    const std::wstring* file_path) {
244    static_cast<MockCrashGenerationServerCallbacks*>(context)->
245      OnClientDumpRequested(client_info, file_path);
246  }
247
248  static void CallOnClientExited(
249    void* context, const google_breakpad::ClientInfo* client_info) {
250    static_cast<MockCrashGenerationServerCallbacks*>(context)->
251      OnClientExited(client_info);
252  }
253
254  static void CallOnClientUploadRequested(void* context, const DWORD crash_id) {
255    static_cast<MockCrashGenerationServerCallbacks*>(context)->
256      OnClientUploadRequested(crash_id);
257  }
258
259  DWORD thread_id_;
260  EXCEPTION_POINTERS* exception_pointers_;
261  MDRawAssertionInfo assert_info_;
262
263  google_breakpad::CrashGenerationServer crash_generation_server_;
264};
265
266TEST_F(CrashGenerationServerTest, PingServerTest) {
267  DoTestFault(CLOSE_AFTER_CONNECT);
268}
269
270TEST_F(CrashGenerationServerTest, InvalidRegistration) {
271  DoTestFault(SEND_INVALID_REGISTRATION);
272}
273
274TEST_F(CrashGenerationServerTest, TruncateRegistration) {
275  DoTestFault(TRUNCATE_REGISTRATION);
276}
277
278TEST_F(CrashGenerationServerTest, CloseAfterRegistration) {
279  DoTestFault(CLOSE_AFTER_REGISTRATION);
280}
281
282TEST_F(CrashGenerationServerTest, ResponseBufferTooSmall) {
283  DoTestFault(RESPONSE_BUFFER_TOO_SMALL);
284}
285
286TEST_F(CrashGenerationServerTest, CloseAfterResponse) {
287  DoTestFault(CLOSE_AFTER_RESPONSE);
288}
289
290// It turns out that, as long as you send one byte, the ACK is accepted and
291// registration succeeds.
292TEST_F(CrashGenerationServerTest, SendInvalidAck) {
293  EXPECT_CALL(mock_callbacks_, OnClientConnected(_));
294  ASSERT_NO_FATAL_FAILURE(FaultyClient(SEND_INVALID_ACK));
295
296  // See DoTestFault for an explanation of this line
297  ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT));
298
299  EXPECT_CALL(mock_callbacks_, OnClientConnected(_));
300  ASSERT_NO_FATAL_FAILURE(FaultyClient(NO_FAULT));
301
302  // See DoTestFault for an explanation of this line
303  ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT));
304}
305
306}  // anonymous namespace