Coverage for /Syzygy/kasko/reporter_unittest.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
98.2%1071090.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/testing/minidump_unittest_helpers.h"
  41    :  #include "syzygy/kasko/testing/test_server.h"
  42    :  #include "syzygy/kasko/testing/upload_observer.h"
  43    :  #include "testing/multiprocess_func_list.h"
  44    :  
  45    :  // The test server will respond to POSTs to /crash by writing all parameters to
  46    :  // a report directory. Each file in the directory has the name of a parameter
  47    :  // and the parameter value as its contents.
  48    :  //
  49    :  // This test instantiates a reporter process, points it at a test server, and
  50    :  // then monitors the server's "incoming" director for new files named
  51    :  // Reporter::kMinidumpUploadFilePart.
  52    :  //
  53    :  // These tests are flaky on the bots. They appear to occasionally hang.
  54    :  // Presumably there is some kind of race condition.
  55    :  // TODO(erikwright): Debug these on the bots, add additional tracing, or do
  56    :  // whatever's necessary to diagnose and deflake these tests.
  57    :  namespace kasko {
  58    :  
  59    :  namespace {
  60    :  
  61    :  const char kCrashKey1Name[] = "foo";
  62    :  const char kCrashKey1Value[] = "bar";
  63    :  const char kCrashKey2Name[] = "hello";
  64    :  const char kCrashKey2Value[] = "world";
  65    :  
  66    :  const char kEndpointSwitch[] = "endpoint";
  67    :  const char kReadyEventSwitch[] = "ready-event";
  68    :  
  69    :  // Signals an event named by kReadyEventSwitch, then blocks indefinitely.
  70  E :  MULTIPROCESS_TEST_MAIN(ReporterTestBlockingProcess) {
  71    :    // Read the caller-supplied parameters.
  72  E :    base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
  73    :    base::string16 ready_event_name =
  74  E :        base::ASCIIToUTF16(cmd_line->GetSwitchValueASCII(kReadyEventSwitch));
  75    :    base::WaitableEvent ready_event(
  76  E :        ::OpenEvent(EVENT_MODIFY_STATE, FALSE, ready_event_name.c_str()));
  77  E :    ready_event.Signal();
  78  E :    ::Sleep(INFINITE);
  79  E :    return 0;
  80  E :  }
  81    :  
  82    :  // Invokes SendDiagnosticReport via the RPC endpoint named by kEndpointSwitch.
  83  E :  MULTIPROCESS_TEST_MAIN(ReporterTestClientProcess) {
  84    :    // Read the caller-supplied parameters.
  85  E :    base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
  86    :    base::string16 endpoint =
  87  E :        base::ASCIIToUTF16(cmd_line->GetSwitchValueASCII(kEndpointSwitch));
  88    :  
  89  E :    common::rpc::ScopedRpcBinding rpc_binding;
  90  E :    if (!rpc_binding.Open(L"ncalrpc", endpoint))
  91  i :      return 1;
  92    :  
  93    :    CrashKey crash_keys[] = {
  94    :        {reinterpret_cast<const signed char*>(kCrashKey1Name),
  95    :         reinterpret_cast<const signed char*>(kCrashKey1Value)},
  96    :        {reinterpret_cast<const signed char*>(kCrashKey2Name),
  97  E :         reinterpret_cast<const signed char*>(kCrashKey2Value)}};
  98    :  
  99  E :    std::string protobuf = "protobuf";
 100    :  
 101    :    common::rpc::RpcStatus status = common::rpc::InvokeRpc(
 102    :        KaskoClient_SendDiagnosticReport, rpc_binding.Get(), NULL, 0, SMALL_DUMP,
 103    :        protobuf.length(), reinterpret_cast<const signed char*>(protobuf.c_str()),
 104  E :        arraysize(crash_keys), crash_keys);
 105  E :    if (status.exception_occurred || !status.succeeded())
 106  i :      return 1;
 107  E :    return 0;
 108  E :  }
 109    :  
 110    :  // Invokes |instance|->SendReportForProcess() using |child_process|.
 111    :  void InvokeSendReportForProcess(Reporter* instance,
 112  E :                                  base::ProcessHandle child_process) {
 113  E :    std::map<base::string16, base::string16> crash_keys_in;
 114    :    crash_keys_in[base::UTF8ToUTF16(kCrashKey1Name)] =
 115  E :        base::UTF8ToUTF16(kCrashKey1Value);
 116    :    crash_keys_in[base::UTF8ToUTF16(kCrashKey2Name)] =
 117  E :        base::UTF8ToUTF16(kCrashKey2Value);
 118    :  
 119  E :    instance->SendReportForProcess(child_process, SMALL_DUMP_TYPE, crash_keys_in);
 120  E :  }
 121    :  
 122    :  // Verifies that the uploaded minidump is plausibly a dump of this test process.
 123    :  void ValidateMinidump(IDebugClient4* debug_client,
 124    :                        IDebugControl* debug_control,
 125  E :                        IDebugSymbols* debug_symbols) {
 126    :    ASSERT_HRESULT_SUCCEEDED(
 127  E :        debug_symbols->GetModuleByModuleName("kasko_unittests", 0, NULL, NULL));
 128  E :  }
 129    :  
 130    :  }  // namespace
 131    :  
 132    :  class ReporterTest : public ::testing::Test {
 133    :   public:
 134  E :    ReporterTest()
 135    :        : test_instance_key_(base::UintToString(base::GetCurrentProcId())) {}
 136    :  
 137  E :    virtual void SetUp() override {
 138  E :      LOG(INFO) << "Starting server.";
 139  E :      ASSERT_TRUE(server_.Start());
 140  E :      LOG(INFO) << "Server started.";
 141  E :      ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
 142  E :      LOG(INFO) << "Temp directory: " << temp_directory_.path().value();
 143  E :    }
 144    :  
 145    :   protected:
 146    :    // Launches a child process that will invoke SendDiagnosticReport using the
 147    :    // RPC endpoint returned by endpoint().
 148  E :    void InvokeRpcFromChildProcess() {
 149    :      base::CommandLine client_command_line =
 150  E :          base::GetMultiProcessTestChildBaseCommandLine();
 151    :      client_command_line.AppendSwitchASCII(switches::kTestChildProcess,
 152  E :                                            "ReporterTestClientProcess");
 153    :      client_command_line.AppendSwitchASCII(kEndpointSwitch,
 154  E :                                            base::UTF16ToASCII(endpoint()));
 155    :      base::ProcessHandle client_process;
 156  E :      ASSERT_TRUE(base::LaunchProcess(client_command_line, base::LaunchOptions(),
 157    :                                      &client_process));
 158    :  
 159  E :      int exit_code = 0;
 160  E :      ASSERT_TRUE(base::WaitForExitCode(client_process, &exit_code));
 161  E :      ASSERT_EQ(0, exit_code);
 162  E :    }
 163    :  
 164    :    // Launches a child process and passes its handle to |callback|. Then kills
 165    :    // the child process.
 166    :    void DoWithChildProcess(
 167  E :        const base::Callback<void(base::ProcessHandle)>& callback) {
 168  E :      std::string ready_event_name = "reporter_test_ready_" + test_instance_key_;
 169    :      base::WaitableEvent ready_event(::CreateEvent(
 170  E :          NULL, FALSE, FALSE, base::ASCIIToUTF16(ready_event_name).c_str()));
 171    :  
 172    :      base::CommandLine child_command_line =
 173  E :          base::GetMultiProcessTestChildBaseCommandLine();
 174    :      child_command_line.AppendSwitchASCII(switches::kTestChildProcess,
 175  E :                                           "ReporterTestBlockingProcess");
 176  E :      child_command_line.AppendSwitchASCII(kReadyEventSwitch, ready_event_name);
 177    :      base::ProcessHandle child_process;
 178  E :      ASSERT_TRUE(base::LaunchProcess(child_command_line, base::LaunchOptions(),
 179    :                                      &child_process));
 180  E :      base::win::ScopedHandle scoped_child_process(child_process);
 181  E :      ready_event.Wait();
 182  E :      callback.Run(child_process);
 183  E :      ASSERT_TRUE(base::KillProcess(child_process, 0, true));
 184  E :    }
 185    :  
 186  E :    uint16_t server_port() { return server_.port(); }
 187    :  
 188  E :    base::string16 endpoint() {
 189  E :      return base::ASCIIToUTF16("reporter_test_endpoint_" + test_instance_key_);
 190  E :    }
 191    :  
 192    :    // This directory is intentionally non-existant to verify that the reporter
 193    :    // creates the target directory as needed.
 194  E :    base::FilePath data_directory() {
 195  E :      return temp_directory_.path().Append(L"Crash Reports");
 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 permanent_failure_directory() {
 201  E :      return temp_directory_.path().Append(L"Permanent Failure");
 202  E :    }
 203    :  
 204  E :    base::FilePath upload_directory() { return server_.incoming_directory(); }
 205    :  
 206    :   private:
 207    :    testing::TestServer server_;
 208    :    base::ScopedTempDir temp_directory_;
 209    :    std::string test_instance_key_;
 210    :  
 211    :    DISALLOW_COPY_AND_ASSIGN(ReporterTest);
 212    :  };
 213    :  
 214  E :  TEST_F(ReporterTest, BasicTest) {
 215    :    scoped_ptr<Reporter> instance(Reporter::Create(
 216    :        endpoint(),
 217    :        L"http://127.0.0.1:" + base::UintToString16(server_port()) + L"/crash",
 218    :        data_directory(), permanent_failure_directory(),
 219    :        base::TimeDelta::FromMilliseconds(1),
 220  E :        base::TimeDelta::FromMilliseconds(1)));
 221    :  
 222  E :    ASSERT_TRUE(instance);
 223    :  
 224    :    testing::UploadObserver upload_observer(upload_directory(),
 225  E :                                            permanent_failure_directory());
 226    :  
 227  E :    ASSERT_NO_FATAL_FAILURE(InvokeRpcFromChildProcess());
 228    :  
 229  E :    base::FilePath minidump_path;
 230  E :    std::map<std::string, std::string> crash_keys;
 231  E :    bool upload_success = false;
 232    :  
 233  E :    upload_observer.WaitForUpload(&minidump_path, &crash_keys, &upload_success);
 234    :  
 235  E :    ASSERT_TRUE(upload_success);
 236    :    EXPECT_HRESULT_SUCCEEDED(
 237  E :        testing::VisitMinidump(minidump_path, base::Bind(&ValidateMinidump)));
 238    :  
 239  E :    Reporter::Shutdown(instance.Pass());
 240  E :  }
 241    :  
 242  E :  TEST_F(ReporterTest, SendReportForProcessTest) {
 243    :    scoped_ptr<Reporter> instance(Reporter::Create(
 244    :        endpoint(),
 245    :        L"http://127.0.0.1:" + base::UintToString16(server_port()) + L"/crash",
 246    :        data_directory(), permanent_failure_directory(),
 247    :        base::TimeDelta::FromMilliseconds(1),
 248  E :        base::TimeDelta::FromMilliseconds(1)));
 249    :  
 250  E :    ASSERT_TRUE(instance);
 251    :  
 252    :    testing::UploadObserver upload_observer(upload_directory(),
 253  E :                                            permanent_failure_directory());
 254    :  
 255    :    ASSERT_NO_FATAL_FAILURE(DoWithChildProcess(base::Bind(
 256  E :        &InvokeSendReportForProcess, base::Unretained(instance.get()))));
 257    :  
 258  E :    base::FilePath minidump_path;
 259  E :    std::map<std::string, std::string> crash_keys;
 260  E :    bool upload_success = false;
 261  E :    upload_observer.WaitForUpload(&minidump_path, &crash_keys, &upload_success);
 262    :  
 263  E :    ASSERT_TRUE(upload_success);
 264    :    EXPECT_HRESULT_SUCCEEDED(
 265  E :        testing::VisitMinidump(minidump_path, base::Bind(&ValidateMinidump)));
 266    :  
 267  E :    Reporter::Shutdown(instance.Pass());
 268  E :  }
 269    :  
 270  E :  TEST_F(ReporterTest, PermanentFailureTest) {
 271    :    scoped_ptr<Reporter> instance(Reporter::Create(
 272    :        endpoint(),
 273    :        L"http://127.0.0.1:" + base::UintToString16(server_port()) +
 274    :            L"/crash_failure",
 275    :        data_directory(), permanent_failure_directory(),
 276    :        base::TimeDelta::FromMilliseconds(1),
 277  E :        base::TimeDelta::FromMilliseconds(1)));
 278    :  
 279  E :    ASSERT_TRUE(instance);
 280    :  
 281    :    testing::UploadObserver upload_observer(upload_directory(),
 282  E :                                            permanent_failure_directory());
 283    :  
 284  E :    ASSERT_NO_FATAL_FAILURE(InvokeRpcFromChildProcess());
 285    :  
 286  E :    base::FilePath minidump_path;
 287  E :    std::map<std::string, std::string> crash_keys;
 288  E :    bool upload_success = false;
 289    :  
 290  E :    upload_observer.WaitForUpload(&minidump_path, &crash_keys, &upload_success);
 291    :  
 292  E :    ASSERT_FALSE(upload_success);
 293    :    EXPECT_HRESULT_SUCCEEDED(
 294  E :        testing::VisitMinidump(minidump_path, base::Bind(&ValidateMinidump)));
 295    :  
 296  E :    Reporter::Shutdown(instance.Pass());
 297  E :  }
 298    :  
 299    :  }  // namespace kasko

Coverage information generated Thu Mar 26 16:15:41 2015.