Coverage for /Syzygy/kasko/reporter_unittest.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
97.2%1411450.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 <string>
  22    :  
  23    :  #include "base/base_switches.h"
  24    :  #include "base/bind.h"
  25    :  #include "base/command_line.h"
  26    :  #include "base/files/file_path.h"
  27    :  #include "base/files/scoped_temp_dir.h"
  28    :  #include "base/memory/scoped_ptr.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    :  namespace kasko {
  60    :  
  61    :  namespace {
  62    :  
  63    :  const base::char16 kCrashKey1Name[] = L"foo";
  64    :  const base::char16 kCrashKey1Value[] = L"bar";
  65    :  const base::char16 kCrashKey2Name[] = L"hello";
  66    :  const base::char16 kCrashKey2Value[] = L"world";
  67    :  
  68    :  const char kEndpointSwitch[] = "endpoint";
  69    :  const char kReadyEventSwitch[] = "ready-event";
  70    :  
  71    :  // Signals an event named by kReadyEventSwitch, then blocks indefinitely.
  72  E :  MULTIPROCESS_TEST_MAIN(ReporterTestBlockingProcess) {
  73    :    // Read the caller-supplied parameters.
  74  E :    base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
  75    :    base::string16 ready_event_name =
  76  E :        base::ASCIIToUTF16(cmd_line->GetSwitchValueASCII(kReadyEventSwitch));
  77    :    base::WaitableEvent ready_event(base::win::ScopedHandle(
  78  E :        ::OpenEvent(EVENT_MODIFY_STATE, FALSE, ready_event_name.c_str())));
  79  E :    ready_event.Signal();
  80  E :    ::Sleep(INFINITE);
  81  E :    return 0;
  82  E :  }
  83    :  
  84    :  // Invokes SendDiagnosticReport via the RPC endpoint named by kEndpointSwitch.
  85  E :  MULTIPROCESS_TEST_MAIN(ReporterTestClientProcess) {
  86    :    // Read the caller-supplied parameters.
  87  E :    base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
  88    :    base::string16 endpoint =
  89  E :        base::ASCIIToUTF16(cmd_line->GetSwitchValueASCII(kEndpointSwitch));
  90  E :    common::rpc::ScopedRpcBinding rpc_binding;
  91  E :    if (!rpc_binding.Open(L"ncalrpc", endpoint)) {
  92  i :      PLOG(ERROR) << "ScopedRpcBinding::Open";
  93  i :      return 1;
  94    :    }
  95    :  
  96    :    CrashKey crash_keys[] = {{kCrashKey1Name, kCrashKey1Value},
  97  E :                             {kCrashKey2Name, kCrashKey2Value}};
  98    :    ::MinidumpRequest rpc_request = {0,          0,       SMALL_DUMP,
  99    :                                     0,          nullptr, arraysize(crash_keys),
 100  E :                                     crash_keys, 0,       nullptr};
 101    :  
 102    :    common::rpc::RpcStatus status = common::rpc::InvokeRpc(
 103  E :        KaskoClient_SendDiagnosticReport, rpc_binding.Get(), rpc_request);
 104  E :    if (status.exception_occurred || !status.succeeded()) {
 105  i :      PLOG(ERROR) << "InvokeRpc";
 106  i :      return 1;
 107    :    }
 108  E :    return 0;
 109  E :  }
 110    :  
 111    :  // Invokes |instance|->SendReportForProcess() using |child_process|.
 112    :  void InvokeSendReportForProcess(Reporter* instance,
 113  E :                                  base::ProcessHandle child_process) {
 114  E :    MinidumpRequest request;
 115    :    request.crash_keys.push_back(
 116  E :        MinidumpRequest::CrashKey(kCrashKey1Name, kCrashKey1Value));
 117    :    request.crash_keys.push_back(
 118  E :        MinidumpRequest::CrashKey(kCrashKey2Name, kCrashKey2Value));
 119    :  
 120  E :    instance->SendReportForProcess(child_process, 0, request);
 121  E :  }
 122    :  
 123    :  // Verifies that the uploaded minidump is plausibly a dump of this test process.
 124    :  void ValidateMinidump(IDebugClient4* debug_client,
 125    :                        IDebugControl* debug_control,
 126  E :                        IDebugSymbols* debug_symbols) {
 127    :    ASSERT_HRESULT_SUCCEEDED(
 128  E :        debug_symbols->GetModuleByModuleName("kasko_unittests", 0, NULL, NULL));
 129  E :  }
 130    :  
 131    :  void OnUpload(base::string16* report_id_out,
 132    :                const base::string16& report_id,
 133    :                const base::FilePath& minidump_path,
 134  E :                const std::map<base::string16, base::string16>& crash_keys) {
 135  E :    *report_id_out = report_id;
 136  E :    ASSERT_FALSE(report_id.empty());
 137  E :    ASSERT_FALSE(minidump_path.empty());
 138  E :  }
 139    :  
 140    :  }  // namespace
 141    :  
 142    :  class ReporterTest : public ::testing::Test {
 143    :   public:
 144  E :    ReporterTest()
 145    :        : test_instance_key_(base::UintToString(base::GetCurrentProcId())) {}
 146    :  
 147  E :    virtual void SetUp() override {
 148  E :      ASSERT_TRUE(server_.Start());
 149  E :      ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
 150  E :    }
 151    :  
 152    :   protected:
 153    :    // Launches a child process that will invoke SendDiagnosticReport using the
 154    :    // RPC endpoint returned by endpoint().
 155  E :    void InvokeRpcFromChildProcess() {
 156    :      base::CommandLine client_command_line =
 157  E :          base::GetMultiProcessTestChildBaseCommandLine();
 158    :      client_command_line.AppendSwitchASCII(switches::kTestChildProcess,
 159  E :                                            "ReporterTestClientProcess");
 160    :      client_command_line.AppendSwitchASCII(kEndpointSwitch,
 161  E :                                            base::UTF16ToASCII(endpoint()));
 162    :      base::Process client_process = base::LaunchProcess(client_command_line,
 163  E :                                                         base::LaunchOptions());
 164  E :      ASSERT_TRUE(client_process.IsValid());
 165    :  
 166  E :      int exit_code = 0;
 167  E :      ASSERT_TRUE(client_process.WaitForExit(&exit_code));
 168  E :      ASSERT_EQ(0, exit_code);
 169  E :    }
 170    :  
 171    :    // Launches a child process and passes its handle to |callback|. Then kills
 172    :    // the child process.
 173    :    void DoWithChildProcess(
 174  E :        const base::Callback<void(base::ProcessHandle)>& callback) {
 175  E :      std::string ready_event_name = "reporter_test_ready_" + test_instance_key_;
 176    :      base::WaitableEvent ready_event(base::win::ScopedHandle(::CreateEvent(
 177  E :          NULL, FALSE, FALSE, base::ASCIIToUTF16(ready_event_name).c_str())));
 178    :  
 179    :      base::CommandLine child_command_line =
 180  E :          base::GetMultiProcessTestChildBaseCommandLine();
 181    :      child_command_line.AppendSwitchASCII(switches::kTestChildProcess,
 182  E :                                           "ReporterTestBlockingProcess");
 183  E :      child_command_line.AppendSwitchASCII(kReadyEventSwitch, ready_event_name);
 184    :      base::Process child_process = base::LaunchProcess(child_command_line,
 185  E :                                                        base::LaunchOptions());
 186  E :      ASSERT_TRUE(child_process.IsValid());
 187  E :      ready_event.Wait();
 188  E :      callback.Run(child_process.Handle());
 189  E :      ASSERT_TRUE(child_process.Terminate(0, true));
 190  E :    }
 191    :  
 192  E :    uint16_t server_port() { return server_.port(); }
 193    :  
 194  E :    base::string16 endpoint() {
 195  E :      return base::ASCIIToUTF16("reporter_test_endpoint_" + test_instance_key_);
 196  E :    }
 197    :  
 198    :    // This directory is intentionally non-existant to verify that the reporter
 199    :    // creates the target directory as needed.
 200  E :    base::FilePath data_directory() {
 201  E :      return temp_directory_.path().Append(L"Crash Reports");
 202  E :    }
 203    :  
 204    :    // This directory is intentionally non-existant to verify that the reporter
 205    :    // creates the target directory as needed.
 206  E :    base::FilePath permanent_failure_directory() {
 207  E :      return temp_directory_.path().Append(L"Permanent Failure");
 208  E :    }
 209    :  
 210  E :    base::FilePath upload_directory() { return server_.incoming_directory(); }
 211    :  
 212    :   private:
 213    :    testing::TestServer server_;
 214    :    base::ScopedTempDir temp_directory_;
 215    :    std::string test_instance_key_;
 216    :  
 217    :    DISALLOW_COPY_AND_ASSIGN(ReporterTest);
 218    :  };
 219    :  
 220  E :  TEST_F(ReporterTest, BasicTest) {
 221  E :    base::string16 report_id;
 222    :    scoped_ptr<Reporter> instance(Reporter::Create(
 223    :        endpoint(),
 224    :        L"http://127.0.0.1:" + base::UintToString16(server_port()) + L"/crash",
 225    :        data_directory(), permanent_failure_directory(),
 226    :        base::TimeDelta::FromMilliseconds(1),
 227    :        base::TimeDelta::FromMilliseconds(1),
 228  E :        base::Bind(&OnUpload, base::Unretained(&report_id))));
 229    :  
 230  E :    ASSERT_TRUE(instance);
 231    :  
 232    :    testing::UploadObserver upload_observer(upload_directory(),
 233  E :                                            permanent_failure_directory());
 234    :  
 235  E :    ASSERT_NO_FATAL_FAILURE(InvokeRpcFromChildProcess());
 236    :  
 237  E :    base::FilePath minidump_path;
 238  E :    std::map<std::string, std::string> crash_keys;
 239  E :    bool upload_success = false;
 240    :  
 241  E :    upload_observer.WaitForUpload(&minidump_path, &crash_keys, &upload_success);
 242    :  
 243  E :    EXPECT_TRUE(upload_success);
 244    :    EXPECT_HRESULT_SUCCEEDED(
 245  E :        testing::VisitMinidump(minidump_path, base::Bind(&ValidateMinidump)));
 246  E :    Reporter::Shutdown(instance.Pass());
 247    :  
 248  E :    auto entry = crash_keys.find(base::UTF16ToASCII(kCrashKey1Name));
 249  E :    ASSERT_NE(entry, crash_keys.end());
 250  E :    ASSERT_EQ(base::UTF16ToASCII(kCrashKey1Value), entry->second);
 251  E :    entry = crash_keys.find(base::UTF16ToASCII(kCrashKey2Name));
 252  E :    ASSERT_NE(entry, crash_keys.end());
 253  E :    ASSERT_EQ(base::UTF16ToASCII(kCrashKey2Value), entry->second);
 254    :    entry =
 255  E :        crash_keys.find(base::UTF16ToASCII(Reporter::kKaskoUploadedByVersion));
 256  E :    ASSERT_NE(entry, crash_keys.end());
 257  E :    ASSERT_EQ(KASKO_VERSION_STRING, entry->second);
 258    :    entry =
 259  E :        crash_keys.find(base::UTF16ToASCII(Reporter::kKaskoGeneratedByVersion));
 260  E :    ASSERT_NE(entry, crash_keys.end());
 261  E :    ASSERT_EQ(KASKO_VERSION_STRING, entry->second);
 262    :  
 263  E :    ASSERT_FALSE(report_id.empty());
 264  E :  }
 265    :  
 266  E :  TEST_F(ReporterTest, NoCallback) {
 267    :    scoped_ptr<Reporter> instance(Reporter::Create(
 268    :        endpoint(),
 269    :        L"http://127.0.0.1:" + base::UintToString16(server_port()) + L"/crash",
 270    :        data_directory(), permanent_failure_directory(),
 271    :        base::TimeDelta::FromMilliseconds(1),
 272  E :        base::TimeDelta::FromMilliseconds(1), Reporter::OnUploadCallback()));
 273    :  
 274  E :    ASSERT_TRUE(instance);
 275    :  
 276    :    testing::UploadObserver upload_observer(upload_directory(),
 277  E :                                            permanent_failure_directory());
 278    :  
 279  E :    ASSERT_NO_FATAL_FAILURE(InvokeRpcFromChildProcess());
 280    :  
 281  E :    base::FilePath minidump_path;
 282  E :    std::map<std::string, std::string> crash_keys;
 283  E :    bool upload_success = false;
 284    :  
 285  E :    upload_observer.WaitForUpload(&minidump_path, &crash_keys, &upload_success);
 286    :  
 287  E :    EXPECT_TRUE(upload_success);
 288    :    EXPECT_HRESULT_SUCCEEDED(
 289  E :        testing::VisitMinidump(minidump_path, base::Bind(&ValidateMinidump)));
 290    :  
 291  E :    Reporter::Shutdown(instance.Pass());
 292  E :  }
 293    :  
 294  E :  TEST_F(ReporterTest, SendReportForProcessTest) {
 295  E :    base::string16 report_id;
 296    :    scoped_ptr<Reporter> instance(Reporter::Create(
 297    :        endpoint(),
 298    :        L"http://127.0.0.1:" + base::UintToString16(server_port()) + L"/crash",
 299    :        data_directory(), permanent_failure_directory(),
 300    :        base::TimeDelta::FromMilliseconds(1),
 301    :        base::TimeDelta::FromMilliseconds(1),
 302  E :        base::Bind(&OnUpload, base::Unretained(&report_id))));
 303    :  
 304  E :    ASSERT_TRUE(instance);
 305    :  
 306    :    testing::UploadObserver upload_observer(upload_directory(),
 307  E :                                            permanent_failure_directory());
 308    :  
 309    :    ASSERT_NO_FATAL_FAILURE(DoWithChildProcess(base::Bind(
 310  E :        &InvokeSendReportForProcess, base::Unretained(instance.get()))));
 311    :  
 312  E :    base::FilePath minidump_path;
 313  E :    std::map<std::string, std::string> crash_keys;
 314  E :    bool upload_success = false;
 315  E :    upload_observer.WaitForUpload(&minidump_path, &crash_keys, &upload_success);
 316    :  
 317  E :    EXPECT_TRUE(upload_success);
 318    :    EXPECT_HRESULT_SUCCEEDED(
 319  E :        testing::VisitMinidump(minidump_path, base::Bind(&ValidateMinidump)));
 320    :  
 321  E :    Reporter::Shutdown(instance.Pass());
 322    :  
 323  E :    ASSERT_FALSE(report_id.empty());
 324  E :  }
 325    :  
 326  E :  TEST_F(ReporterTest, PermanentFailureTest) {
 327  E :    base::string16 report_id;
 328    :    scoped_ptr<Reporter> instance(Reporter::Create(
 329    :        endpoint(), L"http://127.0.0.1:" + base::UintToString16(server_port()) +
 330    :                        L"/crash_failure",
 331    :        data_directory(), permanent_failure_directory(),
 332    :        base::TimeDelta::FromMilliseconds(1),
 333    :        base::TimeDelta::FromMilliseconds(1),
 334  E :        base::Bind(&OnUpload, base::Unretained(&report_id))));
 335    :  
 336  E :    ASSERT_TRUE(instance);
 337    :  
 338    :    testing::UploadObserver upload_observer(upload_directory(),
 339  E :                                            permanent_failure_directory());
 340    :  
 341  E :    ASSERT_NO_FATAL_FAILURE(InvokeRpcFromChildProcess());
 342    :  
 343  E :    base::FilePath minidump_path;
 344  E :    std::map<std::string, std::string> crash_keys;
 345  E :    bool upload_success = false;
 346    :  
 347  E :    upload_observer.WaitForUpload(&minidump_path, &crash_keys, &upload_success);
 348    :  
 349  E :    EXPECT_FALSE(upload_success);
 350    :    EXPECT_HRESULT_SUCCEEDED(
 351  E :        testing::VisitMinidump(minidump_path, base::Bind(&ValidateMinidump)));
 352    :  
 353  E :    Reporter::Shutdown(instance.Pass());
 354    :  
 355  E :    ASSERT_TRUE(report_id.empty());
 356  E :  }
 357    :  
 358    :  }  // namespace kasko

Coverage information generated Thu Jan 14 17:40:38 2016.