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