Coverage for /Syzygy/kasko/reporter_unittest.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
0.0%00226.C++test

Line-by-line coverage:

   1    :  // Copyright 2014 Google Inc. All Rights Reserved.
   2    :  //
   3    :  // Licensed under the Apache License, Version 2.0 (the "License");
   4    :  // you may not use this file except in compliance with the License.
   5    :  // You may obtain a copy of the License at
   6    :  //
   7    :  //     http://www.apache.org/licenses/LICENSE-2.0
   8    :  //
   9    :  // Unless required by applicable law or agreed to in writing, software
  10    :  // distributed under the License is distributed on an "AS IS" BASIS,
  11    :  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12    :  // See the License for the specific language governing permissions and
  13    :  // limitations under the License.
  14    :  
  15    :  #include "syzygy/kasko/reporter.h"
  16    :  
  17    :  #include <Windows.h>  // NOLINT
  18    :  #include <Dbgeng.h>
  19    :  #include <Rpc.h>
  20    :  
  21    :  #include <memory>
  22    :  #include <string>
  23    :  
  24    :  #include "base/base_switches.h"
  25    :  #include "base/bind.h"
  26    :  #include "base/command_line.h"
  27    :  #include "base/files/file_path.h"
  28    :  #include "base/files/scoped_temp_dir.h"
  29    :  #include "base/process/kill.h"
  30    :  #include "base/process/launch.h"
  31    :  #include "base/strings/string16.h"
  32    :  #include "base/strings/string_number_conversions.h"
  33    :  #include "base/strings/utf_string_conversions.h"
  34    :  #include "base/synchronization/waitable_event.h"
  35    :  #include "base/test/multiprocess_test.h"
  36    :  #include "base/time/time.h"
  37    :  #include "gtest/gtest.h"
  38    :  #include "syzygy/common/rpc/helpers.h"
  39    :  #include "syzygy/kasko/kasko_rpc.h"
  40    :  #include "syzygy/kasko/minidump_request.h"
  41    :  #include "syzygy/kasko/version.h"
  42    :  #include "syzygy/kasko/testing/minidump_unittest_helpers.h"
  43    :  #include "syzygy/kasko/testing/test_server.h"
  44    :  #include "syzygy/kasko/testing/upload_observer.h"
  45    :  #include "testing/multiprocess_func_list.h"
  46    :  
  47    :  // The test server will respond to POSTs to /crash by writing all parameters to
  48    :  // a report directory. Each file in the directory has the name of a parameter
  49    :  // and the parameter value as its contents.
  50    :  //
  51    :  // This test instantiates a reporter process, points it at a test server, and
  52    :  // then monitors the server's "incoming" director for new files named
  53    :  // Reporter::kMinidumpUploadFilePart.
  54    :  //
  55    :  // These tests are flaky on the bots. They appear to occasionally hang.
  56    :  // Presumably there is some kind of race condition.
  57    :  // TODO(erikwright): Debug these on the bots, add additional tracing, or do
  58    :  // whatever's necessary to diagnose and deflake these tests.
  59  m :  namespace kasko {
  60    :  
  61  m :  namespace {
  62    :  
  63  m :  const base::char16 kCrashKey1Name[] = L"foo";
  64  m :  const base::char16 kCrashKey1Value[] = L"bar";
  65  m :  const base::char16 kCrashKey2Name[] = L"hello";
  66  m :  const base::char16 kCrashKey2Value[] = L"world";
  67    :  
  68  m :  const char kEndpointSwitch[] = "endpoint";
  69  m :  const char kReadyEventSwitch[] = "ready-event";
  70    :  
  71    :  // Signals an event named by kReadyEventSwitch, then blocks indefinitely.
  72  m :  MULTIPROCESS_TEST_MAIN(ReporterTestBlockingProcess) {
  73    :    // Read the caller-supplied parameters.
  74  m :    base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
  75  m :    base::string16 ready_event_name =
  76  m :        base::ASCIIToUTF16(cmd_line->GetSwitchValueASCII(kReadyEventSwitch));
  77  m :    base::WaitableEvent ready_event(base::win::ScopedHandle(
  78  m :        ::OpenEvent(EVENT_MODIFY_STATE, FALSE, ready_event_name.c_str())));
  79  m :    ready_event.Signal();
  80  m :    ::Sleep(INFINITE);
  81  m :    return 0;
  82  m :  }
  83    :  
  84    :  // Invokes SendDiagnosticReport via the RPC endpoint named by kEndpointSwitch.
  85  m :  MULTIPROCESS_TEST_MAIN(ReporterTestClientProcess) {
  86    :    // Read the caller-supplied parameters.
  87  m :    base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
  88  m :    base::string16 endpoint =
  89  m :        base::ASCIIToUTF16(cmd_line->GetSwitchValueASCII(kEndpointSwitch));
  90  m :    common::rpc::ScopedRpcBinding rpc_binding;
  91  m :    if (!rpc_binding.Open(L"ncalrpc", endpoint)) {
  92  m :      PLOG(ERROR) << "ScopedRpcBinding::Open";
  93  m :      return 1;
  94  m :    }
  95    :  
  96  m :    CrashKey crash_keys[] = {{kCrashKey1Name, kCrashKey1Value},
  97  m :                             {kCrashKey2Name, kCrashKey2Value}};
  98  m :    ::MinidumpRequest rpc_request = {0,          0,       SMALL_DUMP,
  99  m :                                     0,          nullptr, arraysize(crash_keys),
 100  m :                                     crash_keys, 0,       nullptr};
 101    :  
 102  m :    common::rpc::RpcStatus status = common::rpc::InvokeRpc(
 103  m :        KaskoClient_SendDiagnosticReport, rpc_binding.Get(), rpc_request);
 104  m :    if (status.exception_occurred || !status.succeeded()) {
 105  m :      PLOG(ERROR) << "InvokeRpc";
 106  m :      return 1;
 107  m :    }
 108  m :    return 0;
 109  m :  }
 110    :  
 111    :  // Invokes |instance|->SendReportForProcess() using |child_process|.
 112  m :  void InvokeSendReportForProcess(Reporter* instance,
 113  m :                                  base::ProcessHandle child_process) {
 114  m :    MinidumpRequest request;
 115  m :    request.crash_keys.push_back(
 116  m :        MinidumpRequest::CrashKey(kCrashKey1Name, kCrashKey1Value));
 117  m :    request.crash_keys.push_back(
 118  m :        MinidumpRequest::CrashKey(kCrashKey2Name, kCrashKey2Value));
 119    :  
 120  m :    instance->SendReportForProcess(child_process, 0, request);
 121  m :  }
 122    :  
 123    :  // Verifies that the uploaded minidump is plausibly a dump of this test process.
 124  m :  void ValidateMinidump(IDebugClient4* debug_client,
 125  m :                        IDebugControl* debug_control,
 126  m :                        IDebugSymbols* debug_symbols) {
 127  m :    ASSERT_HRESULT_SUCCEEDED(
 128  m :        debug_symbols->GetModuleByModuleName("kasko_unittests", 0, NULL, NULL));
 129  m :  }
 130    :  
 131  m :  void OnUpload(base::string16* report_id_out,
 132  m :                const base::string16& report_id,
 133  m :                const base::FilePath& minidump_path,
 134  m :                const std::map<base::string16, base::string16>& crash_keys) {
 135  m :    *report_id_out = report_id;
 136  m :    ASSERT_FALSE(report_id.empty());
 137  m :    ASSERT_FALSE(minidump_path.empty());
 138  m :  }
 139    :  
 140  m :  }  // namespace
 141    :  
 142  m :  class ReporterTest : public ::testing::Test {
 143  m :   public:
 144  m :    ReporterTest()
 145  m :        : test_instance_key_(base::UintToString(base::GetCurrentProcId())) {}
 146    :  
 147  m :    void SetUp() override {
 148  m :      ASSERT_TRUE(server_.Start());
 149  m :      ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
 150  m :    }
 151    :  
 152  m :   protected:
 153    :    // Launches a child process that will invoke SendDiagnosticReport using the
 154    :    // RPC endpoint returned by endpoint().
 155  m :    void InvokeRpcFromChildProcess() {
 156  m :      base::CommandLine client_command_line =
 157  m :          base::GetMultiProcessTestChildBaseCommandLine();
 158  m :      client_command_line.AppendSwitchASCII(switches::kTestChildProcess,
 159  m :                                            "ReporterTestClientProcess");
 160  m :      client_command_line.AppendSwitchASCII(kEndpointSwitch,
 161  m :                                            base::UTF16ToASCII(endpoint()));
 162  m :      base::Process client_process = base::LaunchProcess(client_command_line,
 163  m :                                                         base::LaunchOptions());
 164  m :      ASSERT_TRUE(client_process.IsValid());
 165    :  
 166  m :      int exit_code = 0;
 167  m :      ASSERT_TRUE(client_process.WaitForExit(&exit_code));
 168  m :      ASSERT_EQ(0, exit_code);
 169  m :    }
 170    :  
 171    :    // Launches a child process and passes its handle to |callback|. Then kills
 172    :    // the child process.
 173  m :    void DoWithChildProcess(
 174  m :        const base::Callback<void(base::ProcessHandle)>& callback) {
 175  m :      std::string ready_event_name = "reporter_test_ready_" + test_instance_key_;
 176  m :      base::WaitableEvent ready_event(base::win::ScopedHandle(::CreateEvent(
 177  m :          NULL, FALSE, FALSE, base::ASCIIToUTF16(ready_event_name).c_str())));
 178    :  
 179  m :      base::CommandLine child_command_line =
 180  m :          base::GetMultiProcessTestChildBaseCommandLine();
 181  m :      child_command_line.AppendSwitchASCII(switches::kTestChildProcess,
 182  m :                                           "ReporterTestBlockingProcess");
 183  m :      child_command_line.AppendSwitchASCII(kReadyEventSwitch, ready_event_name);
 184  m :      base::Process child_process = base::LaunchProcess(child_command_line,
 185  m :                                                        base::LaunchOptions());
 186  m :      ASSERT_TRUE(child_process.IsValid());
 187  m :      ready_event.Wait();
 188  m :      callback.Run(child_process.Handle());
 189  m :      ASSERT_TRUE(child_process.Terminate(0, true));
 190  m :    }
 191    :  
 192  m :    uint16_t server_port() { return server_.port(); }
 193    :  
 194  m :    base::string16 endpoint() {
 195  m :      return base::ASCIIToUTF16("reporter_test_endpoint_" + test_instance_key_);
 196  m :    }
 197    :  
 198    :    // This directory is intentionally non-existant to verify that the reporter
 199    :    // creates the target directory as needed.
 200  m :    base::FilePath data_directory() {
 201  m :      return temp_directory_.path().Append(L"Crash Reports");
 202  m :    }
 203    :  
 204    :    // This directory is intentionally non-existant to verify that the reporter
 205    :    // creates the target directory as needed.
 206  m :    base::FilePath permanent_failure_directory() {
 207  m :      return temp_directory_.path().Append(L"Permanent Failure");
 208  m :    }
 209    :  
 210  m :    base::FilePath upload_directory() { return server_.incoming_directory(); }
 211    :  
 212  m :   private:
 213  m :    testing::TestServer server_;
 214  m :    base::ScopedTempDir temp_directory_;
 215  m :    std::string test_instance_key_;
 216    :  
 217  m :    DISALLOW_COPY_AND_ASSIGN(ReporterTest);
 218  m :  };
 219    :  
 220  m :  TEST_F(ReporterTest, BasicTest) {
 221  m :    base::string16 report_id;
 222  m :    std::unique_ptr<Reporter> instance(Reporter::Create(
 223  m :        endpoint(),
 224  m :        L"http://127.0.0.1:" + base::UintToString16(server_port()) + L"/crash",
 225  m :        data_directory(), permanent_failure_directory(),
 226  m :        base::TimeDelta::FromMilliseconds(1),
 227  m :        base::TimeDelta::FromMilliseconds(1),
 228  m :        base::Bind(&OnUpload, base::Unretained(&report_id))));
 229    :  
 230  m :    ASSERT_TRUE(instance);
 231    :  
 232  m :    testing::UploadObserver upload_observer(upload_directory(),
 233  m :                                            permanent_failure_directory());
 234    :  
 235  m :    ASSERT_NO_FATAL_FAILURE(InvokeRpcFromChildProcess());
 236    :  
 237  m :    base::FilePath minidump_path;
 238  m :    std::map<std::string, std::string> crash_keys;
 239  m :    bool upload_success = false;
 240    :  
 241  m :    upload_observer.WaitForUpload(&minidump_path, &crash_keys, &upload_success);
 242    :  
 243  m :    EXPECT_TRUE(upload_success);
 244  m :    EXPECT_HRESULT_SUCCEEDED(
 245  m :        testing::VisitMinidump(minidump_path, base::Bind(&ValidateMinidump)));
 246  m :    Reporter::Shutdown(std::move(instance));
 247    :  
 248  m :    auto entry = crash_keys.find(base::UTF16ToASCII(kCrashKey1Name));
 249  m :    ASSERT_NE(entry, crash_keys.end());
 250  m :    ASSERT_EQ(base::UTF16ToASCII(kCrashKey1Value), entry->second);
 251  m :    entry = crash_keys.find(base::UTF16ToASCII(kCrashKey2Name));
 252  m :    ASSERT_NE(entry, crash_keys.end());
 253  m :    ASSERT_EQ(base::UTF16ToASCII(kCrashKey2Value), entry->second);
 254  m :    entry =
 255  m :        crash_keys.find(base::UTF16ToASCII(Reporter::kKaskoUploadedByVersion));
 256  m :    ASSERT_NE(entry, crash_keys.end());
 257  m :    ASSERT_EQ(KASKO_VERSION_STRING, entry->second);
 258  m :    entry =
 259  m :        crash_keys.find(base::UTF16ToASCII(Reporter::kKaskoGeneratedByVersion));
 260  m :    ASSERT_NE(entry, crash_keys.end());
 261  m :    ASSERT_EQ(KASKO_VERSION_STRING, entry->second);
 262    :  
 263  m :    ASSERT_FALSE(report_id.empty());
 264  m :  }
 265    :  
 266  m :  TEST_F(ReporterTest, NoCallback) {
 267  m :    std::unique_ptr<Reporter> instance(Reporter::Create(
 268  m :        endpoint(),
 269  m :        L"http://127.0.0.1:" + base::UintToString16(server_port()) + L"/crash",
 270  m :        data_directory(), permanent_failure_directory(),
 271  m :        base::TimeDelta::FromMilliseconds(1),
 272  m :        base::TimeDelta::FromMilliseconds(1), Reporter::OnUploadCallback()));
 273    :  
 274  m :    ASSERT_TRUE(instance);
 275    :  
 276  m :    testing::UploadObserver upload_observer(upload_directory(),
 277  m :                                            permanent_failure_directory());
 278    :  
 279  m :    ASSERT_NO_FATAL_FAILURE(InvokeRpcFromChildProcess());
 280    :  
 281  m :    base::FilePath minidump_path;
 282  m :    std::map<std::string, std::string> crash_keys;
 283  m :    bool upload_success = false;
 284    :  
 285  m :    upload_observer.WaitForUpload(&minidump_path, &crash_keys, &upload_success);
 286    :  
 287  m :    EXPECT_TRUE(upload_success);
 288  m :    EXPECT_HRESULT_SUCCEEDED(
 289  m :        testing::VisitMinidump(minidump_path, base::Bind(&ValidateMinidump)));
 290    :  
 291  m :    Reporter::Shutdown(std::move(instance));
 292  m :  }
 293    :  
 294  m :  TEST_F(ReporterTest, SendReportForProcessTest) {
 295  m :    base::string16 report_id;
 296  m :    std::unique_ptr<Reporter> instance(Reporter::Create(
 297  m :        endpoint(),
 298  m :        L"http://127.0.0.1:" + base::UintToString16(server_port()) + L"/crash",
 299  m :        data_directory(), permanent_failure_directory(),
 300  m :        base::TimeDelta::FromMilliseconds(1),
 301  m :        base::TimeDelta::FromMilliseconds(1),
 302  m :        base::Bind(&OnUpload, base::Unretained(&report_id))));
 303    :  
 304  m :    ASSERT_TRUE(instance);
 305    :  
 306  m :    testing::UploadObserver upload_observer(upload_directory(),
 307  m :                                            permanent_failure_directory());
 308    :  
 309  m :    ASSERT_NO_FATAL_FAILURE(DoWithChildProcess(base::Bind(
 310  m :        &InvokeSendReportForProcess, base::Unretained(instance.get()))));
 311    :  
 312  m :    base::FilePath minidump_path;
 313  m :    std::map<std::string, std::string> crash_keys;
 314  m :    bool upload_success = false;
 315  m :    upload_observer.WaitForUpload(&minidump_path, &crash_keys, &upload_success);
 316    :  
 317  m :    EXPECT_TRUE(upload_success);
 318  m :    EXPECT_HRESULT_SUCCEEDED(
 319  m :        testing::VisitMinidump(minidump_path, base::Bind(&ValidateMinidump)));
 320    :  
 321  m :    Reporter::Shutdown(std::move(instance));
 322    :  
 323  m :    ASSERT_FALSE(report_id.empty());
 324  m :  }
 325    :  
 326  m :  TEST_F(ReporterTest, PermanentFailureTest) {
 327  m :    base::string16 report_id;
 328  m :    std::unique_ptr<Reporter> instance(Reporter::Create(
 329  m :        endpoint(), L"http://127.0.0.1:" + base::UintToString16(server_port()) +
 330  m :                        L"/crash_failure",
 331  m :        data_directory(), permanent_failure_directory(),
 332  m :        base::TimeDelta::FromMilliseconds(1),
 333  m :        base::TimeDelta::FromMilliseconds(1),
 334  m :        base::Bind(&OnUpload, base::Unretained(&report_id))));
 335    :  
 336  m :    ASSERT_TRUE(instance);
 337    :  
 338  m :    testing::UploadObserver upload_observer(upload_directory(),
 339  m :                                            permanent_failure_directory());
 340    :  
 341  m :    ASSERT_NO_FATAL_FAILURE(InvokeRpcFromChildProcess());
 342    :  
 343  m :    base::FilePath minidump_path;
 344  m :    std::map<std::string, std::string> crash_keys;
 345  m :    bool upload_success = false;
 346    :  
 347  m :    upload_observer.WaitForUpload(&minidump_path, &crash_keys, &upload_success);
 348    :  
 349  m :    EXPECT_FALSE(upload_success);
 350  m :    EXPECT_HRESULT_SUCCEEDED(
 351  m :        testing::VisitMinidump(minidump_path, base::Bind(&ValidateMinidump)));
 352    :  
 353  m :    Reporter::Shutdown(std::move(instance));
 354    :  
 355  m :    ASSERT_TRUE(report_id.empty());
 356  m :  }
 357    :  
 358  m :  }  // namespace kasko

Coverage information generated Fri Jul 29 11:00:21 2016.