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