Coverage for /Syzygy/kasko/testing/test_server.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
67.1%57850.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/testing/test_server.h"
  16    :  
  17    :  #include <windows.h>
  18    :  
  19    :  #include <string>
  20    :  #include "base/bind.h"
  21    :  #include "base/command_line.h"
  22    :  #include "base/location.h"
  23    :  #include "base/logging.h"
  24    :  #include "base/process/kill.h"
  25    :  #include "base/process/launch.h"
  26    :  #include "base/strings/string_number_conversions.h"
  27    :  #include "base/strings/utf_string_conversions.h"
  28    :  #include "base/test/test_timeouts.h"
  29    :  #include "base/threading/thread.h"
  30    :  #include "syzygy/common/com_utils.h"
  31    :  #include "syzygy/core/unittest_util.h"
  32    :  
  33    :  namespace kasko {
  34    :  namespace testing {
  35    :  namespace {
  36    :  
  37    :  // Writes |size| bytes to |handle| and sets |*unblocked| to true.
  38    :  // Used as a crude timeout mechanism by ReadData().
  39  i :  void UnblockPipe(HANDLE handle, DWORD size, bool* unblocked) {
  40  i :    std::string unblock_data(size, '\0');
  41    :    // Unblock the ReadFile in LocalTestServer::WaitToStart by writing to the
  42    :    // pipe. Make sure the call succeeded, otherwise we are very likely to hang.
  43  i :    DWORD bytes_written = 0;
  44  i :    LOG(WARNING) << "Timeout reached; unblocking pipe by writing " << size
  45    :                 << " bytes";
  46  i :    CHECK(::WriteFile(handle, unblock_data.data(), size, &bytes_written, NULL));
  47  i :    CHECK_EQ(size, bytes_written);
  48  i :    *unblocked = true;
  49  i :  }
  50    :  
  51    :  // Given a file handle, reads into |buffer| until |bytes_max| bytes
  52    :  // has been read or an error has been encountered.  Returns
  53    :  // true if the read was successful.
  54  E :  bool ReadData(HANDLE read_fd, HANDLE write_fd, DWORD bytes_max, uint8* buffer) {
  55  E :    base::Thread thread("test_server_watcher");
  56  E :    if (!thread.Start())
  57  i :      return false;
  58    :  
  59    :    // Prepare a timeout in case the server fails to start.
  60  E :    bool unblocked = false;
  61    :    thread.message_loop()->PostDelayedTask(
  62    :        FROM_HERE, base::Bind(UnblockPipe, write_fd, bytes_max, &unblocked),
  63  E :        TestTimeouts::action_max_timeout());
  64    :  
  65  E :    DWORD bytes_read = 0;
  66  E :    while (bytes_read < bytes_max) {
  67    :      DWORD num_bytes;
  68    :      if (!::ReadFile(read_fd, buffer + bytes_read, bytes_max - bytes_read,
  69  E :                      &num_bytes, NULL)) {
  70  i :        LOG(ERROR) << "ReadFile failed" << ::common::LogWe();
  71  i :        return false;
  72    :      }
  73  E :      if (num_bytes <= 0) {
  74  i :        LOG(ERROR) << "ReadFile returned invalid byte count: " << num_bytes;
  75  i :        return false;
  76    :      }
  77  E :      bytes_read += num_bytes;
  78  E :    }
  79    :  
  80  E :    thread.Stop();
  81    :    // If the timeout kicked in, abort.
  82  E :    if (unblocked) {
  83  i :      LOG(ERROR) << "Timeout exceeded for ReadData";
  84  i :      return false;
  85    :    }
  86    :  
  87  E :    return true;
  88  E :  }
  89    :  
  90  E :  base::win::ScopedHandle DuplicateStdHandleForInheritance(DWORD std_handle) {
  91  E :    HANDLE original = ::GetStdHandle(std_handle);
  92  E :    if (!original || original == INVALID_HANDLE_VALUE)
  93  i :      return base::win::ScopedHandle();
  94    :  
  95  E :    HANDLE duplicate = nullptr;
  96    :    if (!::DuplicateHandle(base::GetCurrentProcessHandle(), original,
  97    :                    base::GetCurrentProcessHandle(), &duplicate, 0, TRUE,
  98  E :                    DUPLICATE_SAME_ACCESS)) {
  99  i :      LOG(ERROR) << "Duplicating standard handle " << std_handle
 100    :                 << " failed: " << ::common::LogWe();
 101  i :      return base::win::ScopedHandle();
 102    :    }
 103    :  
 104  E :    return base::win::ScopedHandle(duplicate);
 105  E :  }
 106    :  }  // namespace
 107    :  
 108  E :  TestServer::TestServer() : port_(0) {
 109  E :  }
 110    :  
 111  E :  TestServer::~TestServer() {
 112  E :    if (process_handle_) {
 113  E :      if (!base::WaitForSingleProcess(process_handle_.Get(), base::TimeDelta()))
 114  E :        base::KillProcess(process_handle_.Get(), 1, true);
 115    :    }
 116  E :  }
 117    :  
 118  E :  bool TestServer::Start() {
 119  E :    if (!incoming_directory_.CreateUniqueTempDir()) {
 120  i :      LOG(ERROR) << "Failed to create temporary 'incoming' directory.";
 121  i :      return false;
 122    :    }
 123    :  
 124    :    // We will open a pipe used by the child process to indicate its chosen port.
 125  E :    base::win::ScopedHandle read_fd;
 126  E :    base::win::ScopedHandle write_fd;
 127    :    {
 128  E :      HANDLE child_read = NULL;
 129  E :      HANDLE child_write = NULL;
 130  E :      if (!::CreatePipe(&child_read, &child_write, NULL, 0)) {
 131  i :        LOG(ERROR) << "Failed to create pipe" << ::common::LogWe();
 132  i :        return false;
 133    :      }
 134    :  
 135  E :      read_fd.Set(child_read);
 136  E :      write_fd.Set(child_write);
 137    :    }
 138    :  
 139    :    // Have the child inherit the write half.
 140    :    if (!::SetHandleInformation(write_fd.Get(), HANDLE_FLAG_INHERIT,
 141  E :                                HANDLE_FLAG_INHERIT)) {
 142  i :      LOG(ERROR) << "Failed to enable pipe inheritance" << ::common::LogWe();
 143  i :      return false;
 144    :    }
 145    :  
 146    :    base::CommandLine python_command(
 147  E :        ::testing::GetSrcRelativePath(L"third_party/python_26/python.exe"));
 148    :    python_command.AppendArgPath(
 149  E :        ::testing::GetSrcRelativePath(L"syzygy/kasko/testing/test_server.py"));
 150    :  
 151    :    // Pass the handle on the command-line. Although HANDLE is a
 152    :    // pointer, truncating it on 64-bit machines is okay. See
 153    :    // http://msdn.microsoft.com/en-us/library/aa384203.aspx
 154    :    //
 155    :    // "64-bit versions of Windows use 32-bit handles for
 156    :    // interoperability. When sharing a handle between 32-bit and 64-bit
 157    :    // applications, only the lower 32 bits are significant, so it is
 158    :    // safe to truncate the handle (when passing it from 64-bit to
 159    :    // 32-bit) or sign-extend the handle (when passing it from 32-bit to
 160    :    // 64-bit)."
 161    :    python_command.AppendArg(
 162    :        "--startup-pipe=" +
 163  E :        base::IntToString(reinterpret_cast<uintptr_t>(write_fd.Get())));
 164    :  
 165    :    python_command.AppendArg(
 166    :        "--incoming-directory=" +
 167  E :        base::UTF16ToUTF8(incoming_directory_.path().value()));
 168    :  
 169    :    base::win::ScopedHandle stdin_dup =
 170  E :        DuplicateStdHandleForInheritance(STD_INPUT_HANDLE);
 171    :    base::win::ScopedHandle stdout_dup =
 172  E :        DuplicateStdHandleForInheritance(STD_OUTPUT_HANDLE);
 173    :    base::win::ScopedHandle stderr_dup =
 174  E :        DuplicateStdHandleForInheritance(STD_ERROR_HANDLE);
 175    :  
 176  E :    base::LaunchOptions launch_options;
 177  E :    launch_options.inherit_handles = true;
 178  E :    launch_options.stdin_handle = stdin_dup.Get();
 179  E :    launch_options.stdout_handle = stdout_dup.Get();
 180  E :    launch_options.stderr_handle = stderr_dup.Get();
 181    :  
 182  E :    HANDLE process_handle = NULL;
 183  E :    if (!base::LaunchProcess(python_command, launch_options, &process_handle)) {
 184  i :      LOG(ERROR) << "Failed to launch " << python_command.GetCommandLineString();
 185  i :      return false;
 186    :    }
 187  E :    process_handle_.Set(process_handle);
 188    :  
 189    :    if (!ReadData(read_fd.Get(), write_fd.Get(), sizeof(port_),
 190  E :                  reinterpret_cast<uint8*>(&port_))) {
 191  i :      LOG(ERROR) << "Could not read port";
 192  i :      return false;
 193    :    }
 194    :  
 195  E :    return true;
 196  E :  }
 197    :  
 198    :  }  // namespace testing
 199    :  }  // namespace kasko

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