Home | History | Annotate | Download | only in test
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "mojo/edk/test/multiprocess_test_helper.h"
      6 
      7 #include <functional>
      8 #include <set>
      9 #include <utility>
     10 
     11 #include "base/base_switches.h"
     12 #include "base/bind.h"
     13 #include "base/command_line.h"
     14 #include "base/files/file_path.h"
     15 #include "base/logging.h"
     16 #include "base/memory/ref_counted.h"
     17 #include "base/process/kill.h"
     18 #include "base/process/process_handle.h"
     19 #include "base/run_loop.h"
     20 #include "base/strings/stringprintf.h"
     21 #include "base/task_runner.h"
     22 #include "base/threading/thread_task_runner_handle.h"
     23 #include "build/build_config.h"
     24 #include "mojo/edk/embedder/embedder.h"
     25 #include "mojo/edk/embedder/platform_channel_pair.h"
     26 #include "testing/gtest/include/gtest/gtest.h"
     27 
     28 #if defined(OS_WIN)
     29 #include "base/win/windows_version.h"
     30 #elif defined(OS_MACOSX) && !defined(OS_IOS)
     31 #include "base/mac/mach_port_broker.h"
     32 #endif
     33 
     34 namespace mojo {
     35 namespace edk {
     36 namespace test {
     37 
     38 namespace {
     39 
     40 const char kMojoPrimordialPipeToken[] = "mojo-primordial-pipe-token";
     41 
     42 int RunClientFunction(std::function<int(MojoHandle)> handler) {
     43   CHECK(!MultiprocessTestHelper::primordial_pipe_token.empty());
     44   ScopedMessagePipeHandle pipe = CreateChildMessagePipe(
     45       MultiprocessTestHelper::primordial_pipe_token);
     46   return handler(pipe.get().value());
     47 }
     48 
     49 }  // namespace
     50 
     51 MultiprocessTestHelper::MultiprocessTestHelper() {}
     52 
     53 MultiprocessTestHelper::~MultiprocessTestHelper() {
     54   CHECK(!test_child_.IsValid());
     55 }
     56 
     57 ScopedMessagePipeHandle MultiprocessTestHelper::StartChild(
     58     const std::string& test_child_name) {
     59   return StartChildWithExtraSwitch(
     60       test_child_name, std::string(), std::string());
     61 }
     62 
     63 ScopedMessagePipeHandle MultiprocessTestHelper::StartChildWithExtraSwitch(
     64     const std::string& test_child_name,
     65     const std::string& switch_string,
     66     const std::string& switch_value) {
     67   CHECK(!test_child_name.empty());
     68   CHECK(!test_child_.IsValid());
     69 
     70   std::string test_child_main = test_child_name + "TestChildMain";
     71 
     72   // Manually construct the new child's commandline to avoid copying unwanted
     73   // values.
     74   base::CommandLine command_line(
     75       base::GetMultiProcessTestChildBaseCommandLine().GetProgram());
     76 
     77   std::set<std::string> uninherited_args;
     78   uninherited_args.insert("mojo-platform-channel-handle");
     79   uninherited_args.insert(switches::kTestChildProcess);
     80 
     81   // Copy commandline switches from the parent process, except for the
     82   // multiprocess client name and mojo message pipe handle; this allows test
     83   // clients to spawn other test clients.
     84   for (const auto& entry :
     85           base::CommandLine::ForCurrentProcess()->GetSwitches()) {
     86     if (uninherited_args.find(entry.first) == uninherited_args.end())
     87       command_line.AppendSwitchNative(entry.first, entry.second);
     88   }
     89 
     90   PlatformChannelPair channel;
     91   HandlePassingInformation handle_passing_info;
     92   channel.PrepareToPassClientHandleToChildProcess(&command_line,
     93                                                   &handle_passing_info);
     94 
     95   std::string pipe_token = mojo::edk::GenerateRandomToken();
     96   command_line.AppendSwitchASCII(kMojoPrimordialPipeToken, pipe_token);
     97 
     98   if (!switch_string.empty()) {
     99     CHECK(!command_line.HasSwitch(switch_string));
    100     if (!switch_value.empty())
    101       command_line.AppendSwitchASCII(switch_string, switch_value);
    102     else
    103       command_line.AppendSwitch(switch_string);
    104   }
    105 
    106   base::LaunchOptions options;
    107 #if defined(OS_POSIX)
    108   options.fds_to_remap = &handle_passing_info;
    109 #elif defined(OS_WIN)
    110   options.start_hidden = true;
    111   if (base::win::GetVersion() >= base::win::VERSION_VISTA)
    112     options.handles_to_inherit = &handle_passing_info;
    113   else
    114     options.inherit_handles = true;
    115 #else
    116 #error "Not supported yet."
    117 #endif
    118 
    119   std::string child_token = mojo::edk::GenerateRandomToken();
    120   ScopedMessagePipeHandle pipe = CreateParentMessagePipe(pipe_token,
    121                                                          child_token);
    122 
    123   test_child_ =
    124       base::SpawnMultiProcessTestChild(test_child_main, command_line, options);
    125   channel.ChildProcessLaunched();
    126 
    127   ChildProcessLaunched(test_child_.Handle(), channel.PassServerHandle(),
    128                        child_token, process_error_callback_);
    129   CHECK(test_child_.IsValid());
    130 
    131   return pipe;
    132 }
    133 
    134 int MultiprocessTestHelper::WaitForChildShutdown() {
    135   CHECK(test_child_.IsValid());
    136 
    137   int rv = -1;
    138 #if defined(OS_ANDROID)
    139   // On Android, we need to use a special function to wait for the child.
    140   CHECK(AndroidWaitForChildExitWithTimeout(
    141       test_child_, TestTimeouts::action_timeout(), &rv));
    142 #else
    143   CHECK(
    144       test_child_.WaitForExitWithTimeout(TestTimeouts::action_timeout(), &rv));
    145 #endif
    146   test_child_.Close();
    147   return rv;
    148 }
    149 
    150 bool MultiprocessTestHelper::WaitForChildTestShutdown() {
    151   return WaitForChildShutdown() == 0;
    152 }
    153 
    154 // static
    155 void MultiprocessTestHelper::ChildSetup() {
    156   CHECK(base::CommandLine::InitializedForCurrentProcess());
    157 
    158   primordial_pipe_token = base::CommandLine::ForCurrentProcess()
    159       ->GetSwitchValueASCII(kMojoPrimordialPipeToken);
    160   CHECK(!primordial_pipe_token.empty());
    161 
    162 #if defined(OS_MACOSX) && !defined(OS_IOS)
    163   CHECK(base::MachPortBroker::ChildSendTaskPortToParent("mojo_test"));
    164 #endif
    165 
    166   SetParentPipeHandle(
    167       PlatformChannelPair::PassClientHandleFromParentProcess(
    168           *base::CommandLine::ForCurrentProcess()));
    169 }
    170 
    171 // static
    172 int MultiprocessTestHelper::RunClientMain(
    173     const base::Callback<int(MojoHandle)>& main) {
    174   return RunClientFunction([main](MojoHandle handle){
    175     return main.Run(handle);
    176   });
    177 }
    178 
    179 // static
    180 int MultiprocessTestHelper::RunClientTestMain(
    181     const base::Callback<void(MojoHandle)>& main) {
    182   return RunClientFunction([main](MojoHandle handle) {
    183     main.Run(handle);
    184     return (::testing::Test::HasFatalFailure() ||
    185             ::testing::Test::HasNonfatalFailure()) ? 1 : 0;
    186   });
    187 }
    188 
    189 // static
    190 std::string MultiprocessTestHelper::primordial_pipe_token;
    191 
    192 }  // namespace test
    193 }  // namespace edk
    194 }  // namespace mojo
    195