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