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.
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
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"
47 #if defined(OS_MACOSX)
48 #include "chrome/common/mac/mock_launchd.h"
51 #include "base/posix/global_descriptors.h"
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
;
64 using content::BrowserThread
;
68 enum MockServiceProcessExitCodes
{
70 kInitializationFailure
,
75 #if defined(OS_MACOSX)
76 const char kTestExecutablePath
[] = "test-executable-path";
79 bool g_good_shutdown
= false;
82 g_good_shutdown
= true;
83 g_service_process
->Shutdown();
86 class TestStartupClientChannelListener
: public IPC::Listener
{
88 virtual bool OnMessageReceived(const IPC::Message
& message
) OVERRIDE
{
95 class TestServiceProcess
: public ServiceProcess
{
97 TestServiceProcess() { }
98 virtual ~TestServiceProcess() { }
100 bool Initialize(base::MessageLoopForUI
* message_loop
,
101 ServiceProcessState
* state
);
103 base::MessageLoopProxy
* IOMessageLoopProxy() {
104 return io_thread_
->message_loop_proxy().get();
108 bool TestServiceProcess::Initialize(base::MessageLoopForUI
* message_loop
,
109 ServiceProcessState
* state
) {
110 main_message_loop_
= message_loop
;
112 service_process_state_
.reset(state
);
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
);
119 // This mocks the service side IPC message handler, allowing us to have a
120 // minimal service process.
121 class MockServiceIPCServer
: public ServiceIPCServer
{
123 static std::string
EnabledUserId();
125 explicit MockServiceIPCServer(const IPC::ChannelHandle
& handle
)
126 : ServiceIPCServer(handle
),
129 MOCK_METHOD1(OnMessageReceived
, bool(const IPC::Message
& message
));
130 MOCK_METHOD1(OnChannelConnected
, void(int32 peer_pid
));
131 MOCK_METHOD0(OnChannelError
, void());
133 void SetServiceEnabledExpectations();
134 void SetWillBeDisabledExpectations();
136 void CallServiceOnChannelConnected(int32 peer_pid
) {
137 ServiceIPCServer::OnChannelConnected(peer_pid
);
143 cloud_print::CloudPrintProxyInfo info_
;
148 std::string
MockServiceIPCServer::EnabledUserId() {
149 return std::string("kitteh@canhazcheezburger.cat");
152 void MockServiceIPCServer::SetServiceEnabledExpectations() {
153 EXPECT_CALL(*this, OnChannelConnected(_
)).Times(1)
155 Invoke(this, &MockServiceIPCServer::CallServiceOnChannelConnected
));
157 EXPECT_CALL(*this, OnChannelError()).Times(0);
158 EXPECT_CALL(*this, OnMessageReceived(_
)).Times(0);
162 Property(&IPC::Message::type
,
163 static_cast<int32
>(ServiceMsg_GetCloudPrintProxyInfo::ID
))))
164 .Times(AnyNumber()).WillRepeatedly(
165 WithoutArgs(Invoke(this, &MockServiceIPCServer::SendInfo
)));
169 Property(&IPC::Message::type
,
170 static_cast<int32
>(ServiceMsg_Shutdown::ID
))))
173 DoAll(Assign(&g_good_shutdown
, true),
175 Invoke(g_service_process
, &ServiceProcess::Shutdown
)),
179 void MockServiceIPCServer::SetWillBeDisabledExpectations() {
180 SetServiceEnabledExpectations();
184 Property(&IPC::Message::type
,
186 ServiceMsg_DisableCloudPrintProxy::ID
))))
188 .WillRepeatedly(DoAll(Assign(&enabled_
, false), Return(true)));
191 bool MockServiceIPCServer::SendInfo() {
193 info_
.enabled
= true;
194 info_
.email
= EnabledUserId();
195 EXPECT_TRUE(Send(new ServiceHostMsg_CloudPrintProxy_Info(info_
)));
197 info_
.enabled
= false;
198 info_
.email
= std::string();
199 EXPECT_TRUE(Send(new ServiceHostMsg_CloudPrintProxy_Info(info_
)));
204 typedef base::Callback
<void(MockServiceIPCServer
* server
)>
205 SetExpectationsCallback
;
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();
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
);
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
));
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
;
236 TestServiceProcess service_process
;
237 EXPECT_EQ(&service_process
, g_service_process
);
239 // Takes ownership of the pointer, but we can use it since we have the same
241 EXPECT_TRUE(service_process
.Initialize(&main_message_loop
, state
));
243 MockServiceIPCServer
server(state
->GetServiceProcessChannel());
245 // Here is where the expectations/mock responses need to be set up.
246 set_expectations
.Run(&server
);
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();
255 // Connect up the parent/child IPC channel to signal that the test can
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
,
268 service_process
.IOMessageLoopProxy()));
270 main_message_loop
.Run();
271 if (!Mock::VerifyAndClearExpectations(&server
))
272 return kExpectationsNotMet
;
273 if (!g_good_shutdown
)
274 return kShutdownNotGood
;
278 void SetServiceEnabledExpectations(MockServiceIPCServer
* server
) {
279 server
->SetServiceEnabledExpectations();
282 MULTIPROCESS_IPC_TEST_MAIN(CloudPrintMockService_StartEnabledWaitForQuit
) {
283 return CloudPrintMockService_Main(
284 base::Bind(&SetServiceEnabledExpectations
));
287 void SetServiceWillBeDisabledExpectations(MockServiceIPCServer
* server
) {
288 server
->SetWillBeDisabledExpectations();
291 MULTIPROCESS_IPC_TEST_MAIN(CloudPrintMockService_StartEnabledExpectDisabled
) {
292 return CloudPrintMockService_Main(
293 base::Bind(&SetServiceWillBeDisabledExpectations
));
296 class CloudPrintProxyPolicyStartupTest
: public base::MultiProcessTest
,
297 public IPC::Listener
{
299 CloudPrintProxyPolicyStartupTest();
300 virtual ~CloudPrintProxyPolicyStartupTest();
302 virtual void SetUp() OVERRIDE
;
303 virtual void TearDown() OVERRIDE
;
305 scoped_refptr
<base::MessageLoopProxy
> IOMessageLoopProxy() {
306 return BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO
);
308 base::ProcessHandle
Launch(const std::string
& name
);
309 void WaitForConnect();
310 bool Send(IPC::Message
* message
);
311 void ShutdownAndWaitForExitWithTimeout(base::ProcessHandle handle
);
313 // IPC::Listener implementation
314 virtual bool OnMessageReceived(const IPC::Message
& message
) OVERRIDE
{
317 virtual void OnChannelConnected(int32 peer_pid
) OVERRIDE
;
319 // MultiProcessTest implementation.
320 virtual CommandLine
MakeCmdLine(const std::string
& procname
,
321 bool debug_on_start
) OVERRIDE
;
323 bool LaunchBrowser(const CommandLine
& command_line
, Profile
* profile
) {
325 StartupBrowserCreator browser_creator
;
326 return StartupBrowserCreator::ProcessCmdLineImpl(
327 command_line
, base::FilePath(), false, profile
,
328 StartupBrowserCreator::Profiles(), &return_code
, &browser_creator
);
332 content::TestBrowserThreadBundle thread_bundle_
;
333 base::ScopedTempDir temp_user_data_dir_
;
335 std::string startup_channel_id_
;
336 scoped_ptr
<IPC::ChannelProxy
> startup_channel_
;
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_
;
346 class WindowedChannelConnectionObserver
{
348 WindowedChannelConnectionObserver()
356 content::RunMessageLoop();
362 base::MessageLoopForUI::current()->Quit();
370 WindowedChannelConnectionObserver observer_
;
373 CloudPrintProxyPolicyStartupTest::CloudPrintProxyPolicyStartupTest()
374 : thread_bundle_(content::TestBrowserThreadBundle::REAL_IO_THREAD
) {
377 CloudPrintProxyPolicyStartupTest::~CloudPrintProxyPolicyStartupTest() {
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",
388 mock_launchd_
.reset(new MockLaunchd(executable_path_
,
389 base::MessageLoopForUI::current(),
391 scoped_launchd_instance_
.reset(
392 new Launchd::ScopedInstance(mock_launchd_
.get()));
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() << "\".";
408 user_data_dir
= temp_user_data_dir_
.path();
409 command_line
->AppendSwitchPath(switches::kUserDataDir
, user_data_dir
);
411 ASSERT_TRUE(test_launcher_utils::OverrideUserDataDir(user_data_dir
));
414 void CloudPrintProxyPolicyStartupTest::TearDown() {
415 TestingBrowserProcess::DeleteInstance();
418 base::ProcessHandle
CloudPrintProxyPolicyStartupTest::Launch(
419 const std::string
& name
) {
420 EXPECT_FALSE(CheckServiceProcessReady());
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()));
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);
437 base::ProcessHandle handle
= SpawnChild(name
, false);
443 void CloudPrintProxyPolicyStartupTest::WaitForConnect() {
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()));
454 bool CloudPrintProxyPolicyStartupTest::Send(IPC::Message
* message
) {
455 return ServiceProcessControl::GetInstance()->Send(message
);
458 void CloudPrintProxyPolicyStartupTest::ShutdownAndWaitForExitWithTimeout(
459 base::ProcessHandle handle
) {
460 ASSERT_TRUE(Send(new ServiceMsg_Shutdown()));
462 int exit_code
= -100;
464 base::WaitForExitCodeWithTimeout(handle
, &exit_code
,
465 TestTimeouts::action_timeout());
467 EXPECT_EQ(exit_code
, 0);
468 base::CloseProcessHandle(handle
);
471 void CloudPrintProxyPolicyStartupTest::OnChannelConnected(int32 peer_pid
) {
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());
486 TEST_F(CloudPrintProxyPolicyStartupTest
, StartAndShutdown
) {
487 TestingBrowserProcess
* browser_process
=
488 TestingBrowserProcess::GetGlobal();
489 TestingProfileManager
profile_manager(browser_process
);
490 ASSERT_TRUE(profile_manager
.SetUp());
492 // Must be created after the TestingProfileManager since that creates the
493 // LocalState for the BrowserProcess. Must be created before profiles are
495 chrome::TestingIOThreadState testing_io_thread_state
;
497 base::ProcessHandle handle
=
498 Launch("CloudPrintMockService_StartEnabledWaitForQuit");
500 ShutdownAndWaitForExitWithTimeout(handle
);
501 content::RunAllPendingInMessageLoop();
504 BrowserContextKeyedService
* CloudPrintProxyServiceFactoryForPolicyTest(
505 content::BrowserContext
* profile
) {
506 CloudPrintProxyService
* service
=
507 new CloudPrintProxyService(static_cast<Profile
*>(profile
));
508 service
->Initialize();
512 TEST_F(CloudPrintProxyPolicyStartupTest
, StartBrowserWithoutPolicy
) {
513 base::ProcessHandle handle
=
514 Launch("CloudPrintMockService_StartEnabledWaitForQuit");
516 // Setup the Browser Process with a full IOThread::Globals.
517 TestingBrowserProcess
* browser_process
=
518 TestingBrowserProcess::GetGlobal();
520 TestingProfileManager
profile_manager(browser_process
);
521 ASSERT_TRUE(profile_manager
.SetUp());
523 // Must be created after the TestingProfileManager since that creates the
524 // LocalState for the BrowserProcess. Must be created before profiles are
526 chrome::TestingIOThreadState testing_io_thread_state
;
528 TestingProfile
* profile
=
529 profile_manager
.CreateTestingProfile("StartBrowserWithoutPolicy");
530 CloudPrintProxyServiceFactory::GetInstance()->
531 SetTestingFactory(profile
, CloudPrintProxyServiceFactoryForPolicyTest
);
533 TestingPrefServiceSyncable
* prefs
= profile
->GetTestingPrefService();
534 prefs
->SetUserPref(prefs::kCloudPrintEmail
,
535 base::Value::CreateStringValue(
536 MockServiceIPCServer::EnabledUserId()));
538 CommandLine
command_line(CommandLine::NO_PROGRAM
);
539 command_line
.AppendSwitch(switches::kCheckCloudPrintConnectorPolicy
);
540 test_launcher_utils::PrepareBrowserCommandLineForTests(&command_line
);
543 base::RunLoop run_loop
;
544 base::MessageLoop::current()->PostDelayedTask(
546 run_loop
.QuitClosure(),
547 TestTimeouts::action_timeout());
549 bool should_run_loop
= LaunchBrowser(command_line
, profile
);
550 EXPECT_FALSE(should_run_loop
);
554 EXPECT_EQ(MockServiceIPCServer::EnabledUserId(),
555 prefs
->GetString(prefs::kCloudPrintEmail
));
557 ShutdownAndWaitForExitWithTimeout(handle
);
558 content::RunAllPendingInMessageLoop();
559 profile_manager
.DeleteTestingProfile("StartBrowserWithoutPolicy");
562 TEST_F(CloudPrintProxyPolicyStartupTest
, StartBrowserWithPolicy
) {
563 base::ProcessHandle handle
=
564 Launch("CloudPrintMockService_StartEnabledExpectDisabled");
566 TestingBrowserProcess
* browser_process
=
567 TestingBrowserProcess::GetGlobal();
568 TestingProfileManager
profile_manager(browser_process
);
569 ASSERT_TRUE(profile_manager
.SetUp());
571 // Must be created after the TestingProfileManager since that creates the
572 // LocalState for the BrowserProcess. Must be created before profiles are
574 chrome::TestingIOThreadState testing_io_thread_state
;
576 TestingProfile
* profile
=
577 profile_manager
.CreateTestingProfile("StartBrowserWithPolicy");
578 CloudPrintProxyServiceFactory::GetInstance()->
579 SetTestingFactory(profile
, CloudPrintProxyServiceFactoryForPolicyTest
);
581 TestingPrefServiceSyncable
* prefs
= profile
->GetTestingPrefService();
582 prefs
->SetUserPref(prefs::kCloudPrintEmail
,
583 base::Value::CreateStringValue(
584 MockServiceIPCServer::EnabledUserId()));
585 prefs
->SetManagedPref(prefs::kCloudPrintProxyEnabled
,
586 base::Value::CreateBooleanValue(false));
588 CommandLine
command_line(CommandLine::NO_PROGRAM
);
589 command_line
.AppendSwitch(switches::kCheckCloudPrintConnectorPolicy
);
590 test_launcher_utils::PrepareBrowserCommandLineForTests(&command_line
);
593 base::RunLoop run_loop
;
594 base::MessageLoop::current()->PostDelayedTask(
596 run_loop
.QuitClosure(),
597 TestTimeouts::action_timeout());
599 bool should_run_loop
= LaunchBrowser(command_line
, profile
);
601 // No expectations on run_loop being true here; that would be a race
606 EXPECT_EQ("", prefs
->GetString(prefs::kCloudPrintEmail
));
608 ShutdownAndWaitForExitWithTimeout(handle
);
609 content::RunAllPendingInMessageLoop();
610 profile_manager
.DeleteTestingProfile("StartBrowserWithPolicy");