| // Copyright 2017 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/macros.h" | 
 | #include "base/run_loop.h" | 
 | #include "base/strings/utf_string_conversions.h" | 
 | #include "content/browser/renderer_host/render_process_host_impl.h" | 
 | #include "content/browser/utility_process_host.h" | 
 | #include "content/browser/utility_process_host_client.h" | 
 | #include "content/public/browser/browser_thread.h" | 
 | #include "content/public/browser/gpu_service_registry.h" | 
 | #include "content/public/browser/render_frame_host.h" | 
 | #include "content/public/browser/render_process_host.h" | 
 | #include "content/public/browser/web_contents.h" | 
 | #include "content/public/common/service_names.mojom.h" | 
 | #include "content/public/test/browser_test_utils.h" | 
 | #include "content/public/test/content_browser_test.h" | 
 | #include "content/public/test/content_browser_test_utils.h" | 
 | #include "content/shell/browser/shell.h" | 
 | #include "content/shell/common/power_monitor_test.mojom.h" | 
 | #include "mojo/public/cpp/bindings/binding_set.h" | 
 | #include "mojo/public/cpp/bindings/interface_ptr_set.h" | 
 | #include "services/device/public/mojom/constants.mojom.h" | 
 | #include "services/device/public/mojom/power_monitor.mojom.h" | 
 | #include "services/service_manager/public/cpp/service_context.h" | 
 |  | 
 | namespace content { | 
 |  | 
 | namespace { | 
 |  | 
 | void VerifyPowerStateInChildProcess(mojom::PowerMonitorTest* power_monitor_test, | 
 |                                     bool expected_state) { | 
 |   base::RunLoop run_loop; | 
 |   power_monitor_test->QueryNextState(base::BindOnce( | 
 |       [](const base::Closure& quit, bool expected_state, | 
 |          bool on_battery_power) { | 
 |         EXPECT_EQ(expected_state, on_battery_power); | 
 |         quit.Run(); | 
 |       }, | 
 |       run_loop.QuitClosure(), expected_state)); | 
 |   run_loop.Run(); | 
 | } | 
 |  | 
 | void StartUtilityProcessOnIOThread(mojom::PowerMonitorTestRequest request) { | 
 |   UtilityProcessHost* host = | 
 |       new UtilityProcessHost(/*client=*/nullptr, | 
 |                              /*client_task_runner=*/nullptr); | 
 |   host->SetName(base::ASCIIToUTF16("TestProcess")); | 
 |   EXPECT_TRUE(host->Start()); | 
 |  | 
 |   BindInterface(host, std::move(request)); | 
 | } | 
 |  | 
 | void BindInterfaceForGpuOnIOThread(mojom::PowerMonitorTestRequest request) { | 
 |   BindInterfaceInGpuProcess(std::move(request)); | 
 | } | 
 |  | 
 | class MockPowerMonitorMessageBroadcaster : public device::mojom::PowerMonitor { | 
 |  public: | 
 |   MockPowerMonitorMessageBroadcaster() = default; | 
 |   ~MockPowerMonitorMessageBroadcaster() override = default; | 
 |  | 
 |   void Bind(device::mojom::PowerMonitorRequest request) { | 
 |     bindings_.AddBinding(this, std::move(request)); | 
 |   } | 
 |  | 
 |   // device::mojom::PowerMonitor: | 
 |   void AddClient( | 
 |       device::mojom::PowerMonitorClientPtr power_monitor_client) override { | 
 |     power_monitor_client->PowerStateChange(on_battery_power_); | 
 |     clients_.AddPtr(std::move(power_monitor_client)); | 
 |   } | 
 |  | 
 |   void OnPowerStateChange(bool on_battery_power) { | 
 |     on_battery_power_ = on_battery_power; | 
 |     clients_.ForAllPtrs( | 
 |         [&on_battery_power](device::mojom::PowerMonitorClient* client) { | 
 |           client->PowerStateChange(on_battery_power); | 
 |         }); | 
 |   } | 
 |  | 
 |  private: | 
 |   bool on_battery_power_ = false; | 
 |  | 
 |   mojo::BindingSet<device::mojom::PowerMonitor> bindings_; | 
 |   mojo::InterfacePtrSet<device::mojom::PowerMonitorClient> clients_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(MockPowerMonitorMessageBroadcaster); | 
 | }; | 
 |  | 
 | class PowerMonitorTest : public ContentBrowserTest { | 
 |  public: | 
 |   PowerMonitorTest() = default; | 
 |  | 
 |   void SetUp() override { | 
 |     // Because Device Service also runs in this process(browser process), we can | 
 |     // set our binder to intercept requests for PowerMonitor interface to it. | 
 |     service_manager::ServiceContext::SetGlobalBinderForTesting( | 
 |         device::mojom::kServiceName, device::mojom::PowerMonitor::Name_, | 
 |         base::Bind(&PowerMonitorTest::BindPowerMonitor, | 
 |                    base::Unretained(this))); | 
 |  | 
 |     ContentBrowserTest::SetUp(); | 
 |   } | 
 |  | 
 |   void BindPowerMonitor(const std::string& interface_name, | 
 |                         mojo::ScopedMessagePipeHandle handle, | 
 |                         const service_manager::BindSourceInfo& source_info) { | 
 |     if (source_info.identity.name() == mojom::kRendererServiceName) { | 
 |       // We can receive binding requests for the spare RenderProcessHost - this | 
 |       // might happen before the test has provided the | 
 |       // |renderer_bound_closure_|. | 
 |       if (renderer_bound_closure_) { | 
 |         ++request_count_from_renderer_; | 
 |         std::move(renderer_bound_closure_).Run(); | 
 |       } else { | 
 |         DCHECK(RenderProcessHostImpl::GetSpareRenderProcessHostForTesting()); | 
 |       } | 
 |     } else if (source_info.identity.name() == mojom::kUtilityServiceName) { | 
 |       ++request_count_from_utility_; | 
 |  | 
 |       DCHECK(utility_bound_closure_); | 
 |       std::move(utility_bound_closure_).Run(); | 
 |     } else if (source_info.identity.name() == mojom::kGpuServiceName) { | 
 |       ++request_count_from_gpu_; | 
 |  | 
 |       // We ignore null gpu_bound_closure_ here for two possible scenarios: | 
 |       //  - TestRendererProcess and TestUtilityProcess also result in spinning | 
 |       //    up GPU processes as a side effect, but they do not set valid | 
 |       //    gpu_bound_closure_. | 
 |       //  - As GPU process is started during setup of browser test suite, so | 
 |       //    it's possible that TestGpuProcess execution may have not started | 
 |       //    yet when the PowerMonitor bind request comes here, in such case | 
 |       //    gpu_bound_closure_ will also be null. | 
 |       if (gpu_bound_closure_) | 
 |         std::move(gpu_bound_closure_).Run(); | 
 |     } | 
 |  | 
 |     power_monitor_message_broadcaster_.Bind( | 
 |         device::mojom::PowerMonitorRequest(std::move(handle))); | 
 |   } | 
 |  | 
 |  protected: | 
 |   void StartUtilityProcess(mojom::PowerMonitorTestPtr* power_monitor_test, | 
 |                            base::Closure utility_bound_closure) { | 
 |     utility_bound_closure_ = std::move(utility_bound_closure); | 
 |     BrowserThread::PostTask( | 
 |         BrowserThread::IO, FROM_HERE, | 
 |         base::BindOnce(&StartUtilityProcessOnIOThread, | 
 |                        mojo::MakeRequest(power_monitor_test))); | 
 |   } | 
 |  | 
 |   void set_renderer_bound_closure(base::Closure closure) { | 
 |     renderer_bound_closure_ = std::move(closure); | 
 |   } | 
 |  | 
 |   void set_gpu_bound_closure(base::Closure closure) { | 
 |     gpu_bound_closure_ = std::move(closure); | 
 |   } | 
 |  | 
 |   int request_count_from_renderer() { return request_count_from_renderer_; } | 
 |   int request_count_from_utility() { return request_count_from_utility_; } | 
 |   int request_count_from_gpu() { return request_count_from_gpu_; } | 
 |  | 
 |   void SimulatePowerStateChange(bool on_battery_power) { | 
 |     power_monitor_message_broadcaster_.OnPowerStateChange(on_battery_power); | 
 |   } | 
 |  | 
 |  private: | 
 |   int request_count_from_renderer_ = 0; | 
 |   int request_count_from_utility_ = 0; | 
 |   int request_count_from_gpu_ = 0; | 
 |   base::OnceClosure renderer_bound_closure_; | 
 |   base::OnceClosure gpu_bound_closure_; | 
 |   base::OnceClosure utility_bound_closure_; | 
 |  | 
 |   MockPowerMonitorMessageBroadcaster power_monitor_message_broadcaster_; | 
 |  | 
 |   DISALLOW_COPY_AND_ASSIGN(PowerMonitorTest); | 
 | }; | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(PowerMonitorTest, TestRendererProcess) { | 
 |   ASSERT_EQ(0, request_count_from_renderer()); | 
 |   base::RunLoop run_loop; | 
 |   set_renderer_bound_closure(run_loop.QuitClosure()); | 
 |   ASSERT_TRUE(NavigateToURL(shell(), GetTestUrl(".", "simple_page.html"))); | 
 |   run_loop.Run(); | 
 |   EXPECT_EQ(1, request_count_from_renderer()); | 
 |  | 
 |   mojom::PowerMonitorTestPtr power_monitor_renderer; | 
 |   RenderProcessHost* rph = | 
 |       shell()->web_contents()->GetMainFrame()->GetProcess(); | 
 |   BindInterface(rph, &power_monitor_renderer); | 
 |  | 
 |   SimulatePowerStateChange(true); | 
 |   // Verify renderer process on_battery_power changed to true. | 
 |   VerifyPowerStateInChildProcess(power_monitor_renderer.get(), true); | 
 |  | 
 |   SimulatePowerStateChange(false); | 
 |   // Verify renderer process on_battery_power changed to false. | 
 |   VerifyPowerStateInChildProcess(power_monitor_renderer.get(), false); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(PowerMonitorTest, TestUtilityProcess) { | 
 |   mojom::PowerMonitorTestPtr power_monitor_utility; | 
 |  | 
 |   ASSERT_EQ(0, request_count_from_utility()); | 
 |   base::RunLoop run_loop; | 
 |   StartUtilityProcess(&power_monitor_utility, run_loop.QuitClosure()); | 
 |   run_loop.Run(); | 
 |   EXPECT_EQ(1, request_count_from_utility()); | 
 |  | 
 |   SimulatePowerStateChange(true); | 
 |   // Verify utility process on_battery_power changed to true. | 
 |   VerifyPowerStateInChildProcess(power_monitor_utility.get(), true); | 
 |  | 
 |   SimulatePowerStateChange(false); | 
 |   // Verify utility process on_battery_power changed to false. | 
 |   VerifyPowerStateInChildProcess(power_monitor_utility.get(), false); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(PowerMonitorTest, TestGpuProcess) { | 
 |   // As gpu process is started automatically during the setup period of browser | 
 |   // test suite, it may have already started and bound PowerMonitor interface to | 
 |   // Device Service before execution of this TestGpuProcess test. So here we | 
 |   // do not wait for the connection if we found it has already been established. | 
 |   if (request_count_from_gpu() != 1) { | 
 |     ASSERT_EQ(0, request_count_from_gpu()); | 
 |     base::RunLoop run_loop; | 
 |     set_gpu_bound_closure(run_loop.QuitClosure()); | 
 |     // Wait for the connection from gpu process. | 
 |     run_loop.Run(); | 
 |   } | 
 |   EXPECT_EQ(1, request_count_from_gpu()); | 
 |  | 
 |   mojom::PowerMonitorTestPtr power_monitor_gpu; | 
 |   BrowserThread::PostTask( | 
 |       BrowserThread::IO, FROM_HERE, | 
 |       base::BindOnce(&BindInterfaceForGpuOnIOThread, | 
 |                      mojo::MakeRequest(&power_monitor_gpu))); | 
 |  | 
 |   SimulatePowerStateChange(true); | 
 |   // Verify gpu process on_battery_power changed to true. | 
 |   VerifyPowerStateInChildProcess(power_monitor_gpu.get(), true); | 
 |  | 
 |   SimulatePowerStateChange(false); | 
 |   // Verify gpu process on_battery_power changed to false. | 
 |   VerifyPowerStateInChildProcess(power_monitor_gpu.get(), false); | 
 | } | 
 |  | 
 | }  //  namespace | 
 |  | 
 | }  //  namespace content |