1 // Copyright (c) 2012 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 // Create a service process that uses a Mock to respond to the browser in order 6 // to test launching the browser using the cloud print policy check command 7 // line switch. 8 9 #include "base/bind.h" 10 #include "base/command_line.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/process/kill.h" 13 #include "base/rand_util.h" 14 #include "base/synchronization/waitable_event.h" 15 #include "base/test/multiprocess_test.h" 16 #include "base/test/test_timeouts.h" 17 #include "base/time/default_tick_clock.h" 18 #include "base/time/time.h" 19 #include "chrome/browser/prefs/browser_prefs.h" 20 #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h" 21 #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service_factory.h" 22 #include "chrome/browser/service_process/service_process_control.h" 23 #include "chrome/browser/ui/startup/startup_browser_creator.h" 24 #include "chrome/common/chrome_switches.h" 25 #include "chrome/common/pref_names.h" 26 #include "chrome/common/service_messages.h" 27 #include "chrome/common/service_process_util.h" 28 #include "chrome/service/service_ipc_server.h" 29 #include "chrome/service/service_process.h" 30 #include "chrome/test/base/test_launcher_utils.h" 31 #include "chrome/test/base/testing_browser_process.h" 32 #include "chrome/test/base/testing_io_thread_state.h" 33 #include "chrome/test/base/testing_pref_service_syncable.h" 34 #include "chrome/test/base/testing_profile.h" 35 #include "chrome/test/base/testing_profile_manager.h" 36 #include "chrome/test/base/ui_test_utils.h" 37 #include "components/browser_context_keyed_service/browser_context_keyed_service.h" 38 #include "content/public/browser/notification_service.h" 39 #include "content/public/test/test_browser_thread_bundle.h" 40 #include "ipc/ipc_descriptors.h" 41 #include "ipc/ipc_multiprocess_test.h" 42 #include "ipc/ipc_switches.h" 43 #include "testing/gmock/include/gmock/gmock.h" 44 #include "testing/gtest/include/gtest/gtest.h" 45 #include "testing/multiprocess_func_list.h" 46 47 #if defined(OS_MACOSX) 48 #include "chrome/common/mac/mock_launchd.h" 49 #endif 50 #if defined(OS_POSIX) 51 #include "base/posix/global_descriptors.h" 52 #endif 53 54 using ::testing::AnyNumber; 55 using ::testing::Assign; 56 using ::testing::AtLeast; 57 using ::testing::DoAll; 58 using ::testing::Invoke; 59 using ::testing::Mock; 60 using ::testing::Property; 61 using ::testing::Return; 62 using ::testing::WithoutArgs; 63 using ::testing::_; 64 using content::BrowserThread; 65 66 namespace { 67 68 enum MockServiceProcessExitCodes { 69 kMissingSwitch = 1, 70 kInitializationFailure, 71 kExpectationsNotMet, 72 kShutdownNotGood 73 }; 74 75 #if defined(OS_MACOSX) 76 const char kTestExecutablePath[] = "test-executable-path"; 77 #endif 78 79 bool g_good_shutdown = false; 80 81 void ShutdownTask() { 82 g_good_shutdown = true; 83 g_service_process->Shutdown(); 84 } 85 86 class TestStartupClientChannelListener : public IPC::Listener { 87 public: 88 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { 89 return false; 90 } 91 }; 92 93 } // namespace 94 95 class TestServiceProcess : public ServiceProcess { 96 public: 97 TestServiceProcess() { } 98 virtual ~TestServiceProcess() { } 99 100 bool Initialize(base::MessageLoopForUI* message_loop, 101 ServiceProcessState* state); 102 103 base::MessageLoopProxy* IOMessageLoopProxy() { 104 return io_thread_->message_loop_proxy().get(); 105 } 106 }; 107 108 bool TestServiceProcess::Initialize(base::MessageLoopForUI* message_loop, 109 ServiceProcessState* state) { 110 main_message_loop_ = message_loop; 111 112 service_process_state_.reset(state); 113 114 base::Thread::Options options(base::MessageLoop::TYPE_IO, 0); 115 io_thread_.reset(new base::Thread("TestServiceProcess_IO")); 116 return io_thread_->StartWithOptions(options); 117 } 118 119 // This mocks the service side IPC message handler, allowing us to have a 120 // minimal service process. 121 class MockServiceIPCServer : public ServiceIPCServer { 122 public: 123 static std::string EnabledUserId(); 124 125 explicit MockServiceIPCServer(const IPC::ChannelHandle& handle) 126 : ServiceIPCServer(handle), 127 enabled_(true) { } 128 129 MOCK_METHOD1(OnMessageReceived, bool(const IPC::Message& message)); 130 MOCK_METHOD1(OnChannelConnected, void(int32 peer_pid)); 131 MOCK_METHOD0(OnChannelError, void()); 132 133 void SetServiceEnabledExpectations(); 134 void SetWillBeDisabledExpectations(); 135 136 void CallServiceOnChannelConnected(int32 peer_pid) { 137 ServiceIPCServer::OnChannelConnected(peer_pid); 138 } 139 140 bool SendInfo(); 141 142 private: 143 cloud_print::CloudPrintProxyInfo info_; 144 bool enabled_; 145 }; 146 147 // static 148 std::string MockServiceIPCServer::EnabledUserId() { 149 return std::string("kitteh (at) canhazcheezburger.cat"); 150 } 151 152 void MockServiceIPCServer::SetServiceEnabledExpectations() { 153 EXPECT_CALL(*this, OnChannelConnected(_)).Times(1) 154 .WillRepeatedly( 155 Invoke(this, &MockServiceIPCServer::CallServiceOnChannelConnected)); 156 157 EXPECT_CALL(*this, OnChannelError()).Times(0); 158 EXPECT_CALL(*this, OnMessageReceived(_)).Times(0); 159 160 EXPECT_CALL(*this, 161 OnMessageReceived( 162 Property(&IPC::Message::type, 163 static_cast<int32>(ServiceMsg_GetCloudPrintProxyInfo::ID)))) 164 .Times(AnyNumber()).WillRepeatedly( 165 WithoutArgs(Invoke(this, &MockServiceIPCServer::SendInfo))); 166 167 EXPECT_CALL(*this, 168 OnMessageReceived( 169 Property(&IPC::Message::type, 170 static_cast<int32>(ServiceMsg_Shutdown::ID)))) 171 .Times(1) 172 .WillOnce( 173 DoAll(Assign(&g_good_shutdown, true), 174 WithoutArgs( 175 Invoke(g_service_process, &ServiceProcess::Shutdown)), 176 Return(true))); 177 } 178 179 void MockServiceIPCServer::SetWillBeDisabledExpectations() { 180 SetServiceEnabledExpectations(); 181 182 EXPECT_CALL(*this, 183 OnMessageReceived( 184 Property(&IPC::Message::type, 185 static_cast<int32>( 186 ServiceMsg_DisableCloudPrintProxy::ID)))) 187 .Times(AtLeast(1)) 188 .WillRepeatedly(DoAll(Assign(&enabled_, false), Return(true))); 189 } 190 191 bool MockServiceIPCServer::SendInfo() { 192 if (enabled_) { 193 info_.enabled = true; 194 info_.email = EnabledUserId(); 195 EXPECT_TRUE(Send(new ServiceHostMsg_CloudPrintProxy_Info(info_))); 196 } else { 197 info_.enabled = false; 198 info_.email = std::string(); 199 EXPECT_TRUE(Send(new ServiceHostMsg_CloudPrintProxy_Info(info_))); 200 } 201 return true; 202 } 203 204 typedef base::Callback<void(MockServiceIPCServer* server)> 205 SetExpectationsCallback; 206 207 // The return value from this routine is used as the exit code for the mock 208 // service process. Any non-zero return value will be printed out and can help 209 // determine the failure. 210 int CloudPrintMockService_Main(SetExpectationsCallback set_expectations) { 211 base::MessageLoopForUI main_message_loop; 212 main_message_loop.set_thread_name("Main Thread"); 213 CommandLine* command_line = CommandLine::ForCurrentProcess(); 214 215 #if defined(OS_MACOSX) 216 if (!command_line->HasSwitch(kTestExecutablePath)) 217 return kMissingSwitch; 218 base::FilePath executable_path = 219 command_line->GetSwitchValuePath(kTestExecutablePath); 220 EXPECT_FALSE(executable_path.empty()); 221 MockLaunchd mock_launchd(executable_path, &main_message_loop, true, true); 222 Launchd::ScopedInstance use_mock(&mock_launchd); 223 #endif 224 225 base::FilePath user_data_dir = 226 command_line->GetSwitchValuePath(switches::kUserDataDir); 227 CHECK(!user_data_dir.empty()); 228 CHECK(test_launcher_utils::OverrideUserDataDir(user_data_dir)); 229 230 ServiceProcessState* state(new ServiceProcessState); 231 bool service_process_state_initialized = state->Initialize(); 232 EXPECT_TRUE(service_process_state_initialized); 233 if (!service_process_state_initialized) 234 return kInitializationFailure; 235 236 TestServiceProcess service_process; 237 EXPECT_EQ(&service_process, g_service_process); 238 239 // Takes ownership of the pointer, but we can use it since we have the same 240 // lifetime. 241 EXPECT_TRUE(service_process.Initialize(&main_message_loop, state)); 242 243 MockServiceIPCServer server(state->GetServiceProcessChannel()); 244 245 // Here is where the expectations/mock responses need to be set up. 246 set_expectations.Run(&server); 247 248 EXPECT_TRUE(server.Init()); 249 EXPECT_TRUE(state->SignalReady(service_process.IOMessageLoopProxy(), 250 base::Bind(&ShutdownTask))); 251 #if defined(OS_MACOSX) 252 mock_launchd.SignalReady(); 253 #endif 254 255 // Connect up the parent/child IPC channel to signal that the test can 256 // continue. 257 TestStartupClientChannelListener listener; 258 EXPECT_TRUE(CommandLine::ForCurrentProcess()->HasSwitch( 259 switches::kProcessChannelID)); 260 std::string startup_channel_name = 261 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 262 switches::kProcessChannelID); 263 scoped_ptr<IPC::ChannelProxy> startup_channel; 264 startup_channel.reset( 265 new IPC::ChannelProxy(startup_channel_name, 266 IPC::Channel::MODE_CLIENT, 267 &listener, 268 service_process.IOMessageLoopProxy())); 269 270 main_message_loop.Run(); 271 if (!Mock::VerifyAndClearExpectations(&server)) 272 return kExpectationsNotMet; 273 if (!g_good_shutdown) 274 return kShutdownNotGood; 275 return 0; 276 } 277 278 void SetServiceEnabledExpectations(MockServiceIPCServer* server) { 279 server->SetServiceEnabledExpectations(); 280 } 281 282 MULTIPROCESS_IPC_TEST_MAIN(CloudPrintMockService_StartEnabledWaitForQuit) { 283 return CloudPrintMockService_Main( 284 base::Bind(&SetServiceEnabledExpectations)); 285 } 286 287 void SetServiceWillBeDisabledExpectations(MockServiceIPCServer* server) { 288 server->SetWillBeDisabledExpectations(); 289 } 290 291 MULTIPROCESS_IPC_TEST_MAIN(CloudPrintMockService_StartEnabledExpectDisabled) { 292 return CloudPrintMockService_Main( 293 base::Bind(&SetServiceWillBeDisabledExpectations)); 294 } 295 296 class CloudPrintProxyPolicyStartupTest : public base::MultiProcessTest, 297 public IPC::Listener { 298 public: 299 CloudPrintProxyPolicyStartupTest(); 300 virtual ~CloudPrintProxyPolicyStartupTest(); 301 302 virtual void SetUp() OVERRIDE; 303 virtual void TearDown() OVERRIDE; 304 305 scoped_refptr<base::MessageLoopProxy> IOMessageLoopProxy() { 306 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO); 307 } 308 base::ProcessHandle Launch(const std::string& name); 309 void WaitForConnect(); 310 bool Send(IPC::Message* message); 311 void ShutdownAndWaitForExitWithTimeout(base::ProcessHandle handle); 312 313 // IPC::Listener implementation 314 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { 315 return false; 316 } 317 virtual void OnChannelConnected(int32 peer_pid) OVERRIDE; 318 319 // MultiProcessTest implementation. 320 virtual CommandLine MakeCmdLine(const std::string& procname, 321 bool debug_on_start) OVERRIDE; 322 323 bool LaunchBrowser(const CommandLine& command_line, Profile* profile) { 324 int return_code = 0; 325 StartupBrowserCreator browser_creator; 326 return StartupBrowserCreator::ProcessCmdLineImpl( 327 command_line, base::FilePath(), false, profile, 328 StartupBrowserCreator::Profiles(), &return_code, &browser_creator); 329 } 330 331 protected: 332 content::TestBrowserThreadBundle thread_bundle_; 333 base::ScopedTempDir temp_user_data_dir_; 334 335 std::string startup_channel_id_; 336 scoped_ptr<IPC::ChannelProxy> startup_channel_; 337 338 #if defined(OS_MACOSX) 339 base::ScopedTempDir temp_dir_; 340 base::FilePath executable_path_, bundle_path_; 341 scoped_ptr<MockLaunchd> mock_launchd_; 342 scoped_ptr<Launchd::ScopedInstance> scoped_launchd_instance_; 343 #endif 344 345 private: 346 class WindowedChannelConnectionObserver { 347 public: 348 WindowedChannelConnectionObserver() 349 : seen_(false), 350 running_(false) { } 351 352 void Wait() { 353 if (seen_) 354 return; 355 running_ = true; 356 content::RunMessageLoop(); 357 } 358 359 void Notify() { 360 seen_ = true; 361 if (running_) 362 base::MessageLoopForUI::current()->Quit(); 363 } 364 365 private: 366 bool seen_; 367 bool running_; 368 }; 369 370 WindowedChannelConnectionObserver observer_; 371 }; 372 373 CloudPrintProxyPolicyStartupTest::CloudPrintProxyPolicyStartupTest() 374 : thread_bundle_(content::TestBrowserThreadBundle::REAL_IO_THREAD) { 375 } 376 377 CloudPrintProxyPolicyStartupTest::~CloudPrintProxyPolicyStartupTest() { 378 } 379 380 void CloudPrintProxyPolicyStartupTest::SetUp() { 381 TestingBrowserProcess::CreateInstance(); 382 #if defined(OS_MACOSX) 383 EXPECT_TRUE(temp_dir_.CreateUniqueTempDir()); 384 EXPECT_TRUE(MockLaunchd::MakeABundle(temp_dir_.path(), 385 "CloudPrintProxyTest", 386 &bundle_path_, 387 &executable_path_)); 388 mock_launchd_.reset(new MockLaunchd(executable_path_, 389 base::MessageLoopForUI::current(), 390 true, false)); 391 scoped_launchd_instance_.reset( 392 new Launchd::ScopedInstance(mock_launchd_.get())); 393 #endif 394 395 // Ensure test does not use the standard profile directory. This is copied 396 // from InProcessBrowserTest::SetUp(). These tests require a more complex 397 // process startup so they are unable to just inherit from 398 // InProcessBrowserTest. 399 CommandLine* command_line = CommandLine::ForCurrentProcess(); 400 base::FilePath user_data_dir = 401 command_line->GetSwitchValuePath(switches::kUserDataDir); 402 if (user_data_dir.empty()) { 403 ASSERT_TRUE(temp_user_data_dir_.CreateUniqueTempDir() && 404 temp_user_data_dir_.IsValid()) 405 << "Could not create temporary user data directory \"" 406 << temp_user_data_dir_.path().value() << "\"."; 407 408 user_data_dir = temp_user_data_dir_.path(); 409 command_line->AppendSwitchPath(switches::kUserDataDir, user_data_dir); 410 } 411 ASSERT_TRUE(test_launcher_utils::OverrideUserDataDir(user_data_dir)); 412 } 413 414 void CloudPrintProxyPolicyStartupTest::TearDown() { 415 TestingBrowserProcess::DeleteInstance(); 416 } 417 418 base::ProcessHandle CloudPrintProxyPolicyStartupTest::Launch( 419 const std::string& name) { 420 EXPECT_FALSE(CheckServiceProcessReady()); 421 422 startup_channel_id_ = 423 base::StringPrintf("%d.%p.%d", 424 base::GetCurrentProcId(), this, 425 base::RandInt(0, std::numeric_limits<int>::max())); 426 startup_channel_.reset(new IPC::ChannelProxy( 427 startup_channel_id_, IPC::Channel::MODE_SERVER, 428 this, IOMessageLoopProxy())); 429 430 #if defined(OS_POSIX) 431 base::FileHandleMappingVector ipc_file_list; 432 ipc_file_list.push_back(std::make_pair( 433 startup_channel_->TakeClientFileDescriptor(), 434 kPrimaryIPCChannel + base::GlobalDescriptors::kBaseDescriptor)); 435 base::ProcessHandle handle = SpawnChild(name, ipc_file_list, false); 436 #else 437 base::ProcessHandle handle = SpawnChild(name, false); 438 #endif 439 EXPECT_TRUE(handle); 440 return handle; 441 } 442 443 void CloudPrintProxyPolicyStartupTest::WaitForConnect() { 444 observer_.Wait(); 445 EXPECT_TRUE(CheckServiceProcessReady()); 446 EXPECT_TRUE(base::MessageLoopProxy::current().get()); 447 ServiceProcessControl::GetInstance()->SetChannel( 448 new IPC::ChannelProxy(GetServiceProcessChannel(), 449 IPC::Channel::MODE_NAMED_CLIENT, 450 ServiceProcessControl::GetInstance(), 451 IOMessageLoopProxy())); 452 } 453 454 bool CloudPrintProxyPolicyStartupTest::Send(IPC::Message* message) { 455 return ServiceProcessControl::GetInstance()->Send(message); 456 } 457 458 void CloudPrintProxyPolicyStartupTest::ShutdownAndWaitForExitWithTimeout( 459 base::ProcessHandle handle) { 460 ASSERT_TRUE(Send(new ServiceMsg_Shutdown())); 461 462 int exit_code = -100; 463 bool exited = 464 base::WaitForExitCodeWithTimeout(handle, &exit_code, 465 TestTimeouts::action_timeout()); 466 EXPECT_TRUE(exited); 467 EXPECT_EQ(exit_code, 0); 468 base::CloseProcessHandle(handle); 469 } 470 471 void CloudPrintProxyPolicyStartupTest::OnChannelConnected(int32 peer_pid) { 472 observer_.Notify(); 473 } 474 475 CommandLine CloudPrintProxyPolicyStartupTest::MakeCmdLine( 476 const std::string& procname, 477 bool debug_on_start) { 478 CommandLine cl = MultiProcessTest::MakeCmdLine(procname, debug_on_start); 479 cl.AppendSwitchASCII(switches::kProcessChannelID, startup_channel_id_); 480 #if defined(OS_MACOSX) 481 cl.AppendSwitchASCII(kTestExecutablePath, executable_path_.value()); 482 #endif 483 return cl; 484 } 485 486 TEST_F(CloudPrintProxyPolicyStartupTest, StartAndShutdown) { 487 TestingBrowserProcess* browser_process = 488 TestingBrowserProcess::GetGlobal(); 489 TestingProfileManager profile_manager(browser_process); 490 ASSERT_TRUE(profile_manager.SetUp()); 491 492 // Must be created after the TestingProfileManager since that creates the 493 // LocalState for the BrowserProcess. Must be created before profiles are 494 // constructed. 495 chrome::TestingIOThreadState testing_io_thread_state; 496 497 base::ProcessHandle handle = 498 Launch("CloudPrintMockService_StartEnabledWaitForQuit"); 499 WaitForConnect(); 500 ShutdownAndWaitForExitWithTimeout(handle); 501 content::RunAllPendingInMessageLoop(); 502 } 503 504 BrowserContextKeyedService* CloudPrintProxyServiceFactoryForPolicyTest( 505 content::BrowserContext* profile) { 506 CloudPrintProxyService* service = 507 new CloudPrintProxyService(static_cast<Profile*>(profile)); 508 service->Initialize(); 509 return service; 510 } 511 512 TEST_F(CloudPrintProxyPolicyStartupTest, StartBrowserWithoutPolicy) { 513 base::ProcessHandle handle = 514 Launch("CloudPrintMockService_StartEnabledWaitForQuit"); 515 516 // Setup the Browser Process with a full IOThread::Globals. 517 TestingBrowserProcess* browser_process = 518 TestingBrowserProcess::GetGlobal(); 519 520 TestingProfileManager profile_manager(browser_process); 521 ASSERT_TRUE(profile_manager.SetUp()); 522 523 // Must be created after the TestingProfileManager since that creates the 524 // LocalState for the BrowserProcess. Must be created before profiles are 525 // constructed. 526 chrome::TestingIOThreadState testing_io_thread_state; 527 528 TestingProfile* profile = 529 profile_manager.CreateTestingProfile("StartBrowserWithoutPolicy"); 530 CloudPrintProxyServiceFactory::GetInstance()-> 531 SetTestingFactory(profile, CloudPrintProxyServiceFactoryForPolicyTest); 532 533 TestingPrefServiceSyncable* prefs = profile->GetTestingPrefService(); 534 prefs->SetUserPref(prefs::kCloudPrintEmail, 535 Value::CreateStringValue( 536 MockServiceIPCServer::EnabledUserId())); 537 538 CommandLine command_line(CommandLine::NO_PROGRAM); 539 command_line.AppendSwitch(switches::kCheckCloudPrintConnectorPolicy); 540 test_launcher_utils::PrepareBrowserCommandLineForTests(&command_line); 541 542 WaitForConnect(); 543 base::RunLoop run_loop; 544 base::MessageLoop::current()->PostDelayedTask( 545 FROM_HERE, 546 run_loop.QuitClosure(), 547 TestTimeouts::action_timeout()); 548 549 bool should_run_loop = LaunchBrowser(command_line, profile); 550 EXPECT_FALSE(should_run_loop); 551 if (should_run_loop) 552 run_loop.Run(); 553 554 EXPECT_EQ(MockServiceIPCServer::EnabledUserId(), 555 prefs->GetString(prefs::kCloudPrintEmail)); 556 557 ShutdownAndWaitForExitWithTimeout(handle); 558 content::RunAllPendingInMessageLoop(); 559 profile_manager.DeleteTestingProfile("StartBrowserWithoutPolicy"); 560 } 561 562 TEST_F(CloudPrintProxyPolicyStartupTest, StartBrowserWithPolicy) { 563 base::ProcessHandle handle = 564 Launch("CloudPrintMockService_StartEnabledExpectDisabled"); 565 566 TestingBrowserProcess* browser_process = 567 TestingBrowserProcess::GetGlobal(); 568 TestingProfileManager profile_manager(browser_process); 569 ASSERT_TRUE(profile_manager.SetUp()); 570 571 // Must be created after the TestingProfileManager since that creates the 572 // LocalState for the BrowserProcess. Must be created before profiles are 573 // constructed. 574 chrome::TestingIOThreadState testing_io_thread_state; 575 576 TestingProfile* profile = 577 profile_manager.CreateTestingProfile("StartBrowserWithPolicy"); 578 CloudPrintProxyServiceFactory::GetInstance()-> 579 SetTestingFactory(profile, CloudPrintProxyServiceFactoryForPolicyTest); 580 581 TestingPrefServiceSyncable* prefs = profile->GetTestingPrefService(); 582 prefs->SetUserPref(prefs::kCloudPrintEmail, 583 Value::CreateStringValue( 584 MockServiceIPCServer::EnabledUserId())); 585 prefs->SetManagedPref(prefs::kCloudPrintProxyEnabled, 586 Value::CreateBooleanValue(false)); 587 588 CommandLine command_line(CommandLine::NO_PROGRAM); 589 command_line.AppendSwitch(switches::kCheckCloudPrintConnectorPolicy); 590 test_launcher_utils::PrepareBrowserCommandLineForTests(&command_line); 591 592 WaitForConnect(); 593 base::RunLoop run_loop; 594 base::MessageLoop::current()->PostDelayedTask( 595 FROM_HERE, 596 run_loop.QuitClosure(), 597 TestTimeouts::action_timeout()); 598 599 bool should_run_loop = LaunchBrowser(command_line, profile); 600 601 // No expectations on run_loop being true here; that would be a race 602 // condition. 603 if (should_run_loop) 604 run_loop.Run(); 605 606 EXPECT_EQ("", prefs->GetString(prefs::kCloudPrintEmail)); 607 608 ShutdownAndWaitForExitWithTimeout(handle); 609 content::RunAllPendingInMessageLoop(); 610 profile_manager.DeleteTestingProfile("StartBrowserWithPolicy"); 611 } 612