1 // Copyright 2017 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 #include <utility>
6 
7 #include "base/bind.h"
8 #include "base/callback_helpers.h"
9 #include "base/macros.h"
10 #include "content/browser/renderer_host/render_process_host_impl.h"
11 #include "content/public/browser/web_contents.h"
12 #include "content/public/test/browser_test.h"
13 #include "content/public/test/browser_test_utils.h"
14 #include "content/public/test/content_browser_test.h"
15 #include "content/public/test/content_browser_test_utils.h"
16 #include "content/public/test/test_navigation_observer.h"
17 #include "content/public/test/test_utils.h"
18 #include "content/shell/browser/shell.h"
19 #include "mojo/public/cpp/bindings/pending_receiver.h"
20 #include "mojo/public/cpp/bindings/receiver.h"
21 #include "services/device/public/mojom/battery_monitor.mojom.h"
22 #include "services/device/public/mojom/battery_status.mojom.h"
23 
24 namespace content {
25 
26 namespace {
27 
28 class MockBatteryMonitor : public device::mojom::BatteryMonitor {
29  public:
30   MockBatteryMonitor() = default;
31   ~MockBatteryMonitor() override = default;
32 
Bind(mojo::PendingReceiver<device::mojom::BatteryMonitor> receiver)33   void Bind(mojo::PendingReceiver<device::mojom::BatteryMonitor> receiver) {
34     DCHECK(!receiver_.is_bound());
35     receiver_.Bind(std::move(receiver));
36   }
37 
DidChange(const device::mojom::BatteryStatus & battery_status)38   void DidChange(const device::mojom::BatteryStatus& battery_status) {
39     status_ = battery_status;
40     status_to_report_ = true;
41 
42     if (!callback_.is_null())
43       ReportStatus();
44   }
45 
46  private:
47   // mojom::BatteryMonitor methods:
QueryNextStatus(QueryNextStatusCallback callback)48   void QueryNextStatus(QueryNextStatusCallback callback) override {
49     if (!callback_.is_null()) {
50       DVLOG(1) << "Overlapped call to QueryNextStatus!";
51       receiver_.reset();
52       return;
53     }
54     callback_ = std::move(callback);
55 
56     if (status_to_report_)
57       ReportStatus();
58   }
59 
ReportStatus()60   void ReportStatus() {
61     std::move(callback_).Run(status_.Clone());
62     status_to_report_ = false;
63   }
64 
65   QueryNextStatusCallback callback_;
66   device::mojom::BatteryStatus status_;
67   bool status_to_report_ = false;
68   mojo::Receiver<device::mojom::BatteryMonitor> receiver_{this};
69 
70   DISALLOW_COPY_AND_ASSIGN(MockBatteryMonitor);
71 };
72 
73 class BatteryMonitorTest : public ContentBrowserTest {
74  public:
BatteryMonitorTest()75   BatteryMonitorTest() {
76     mock_battery_monitor_ = std::make_unique<MockBatteryMonitor>();
77     // Because Device Service also runs in this process(browser process), here
78     // we can directly set our binder to intercept interface requests against
79     // it.
80     RenderProcessHostImpl::OverrideBatteryMonitorBinderForTesting(
81         base::BindRepeating(&MockBatteryMonitor::Bind,
82                             base::Unretained(mock_battery_monitor_.get())));
83   }
84 
~BatteryMonitorTest()85   ~BatteryMonitorTest() override {
86     RenderProcessHostImpl::OverrideBatteryMonitorBinderForTesting(
87         base::NullCallback());
88   }
89 
90  protected:
mock_battery_monitor()91   MockBatteryMonitor* mock_battery_monitor() {
92     return mock_battery_monitor_.get();
93   }
94 
95  private:
96   std::unique_ptr<MockBatteryMonitor> mock_battery_monitor_;
97 
98   DISALLOW_COPY_AND_ASSIGN(BatteryMonitorTest);
99 };
100 
IN_PROC_BROWSER_TEST_F(BatteryMonitorTest,NavigatorGetBatteryInfo)101 IN_PROC_BROWSER_TEST_F(BatteryMonitorTest, NavigatorGetBatteryInfo) {
102   // From JavaScript request a promise for the battery status information and
103   // once it resolves check the values and navigate to #pass.
104   device::mojom::BatteryStatus status;
105   status.charging = true;
106   status.charging_time = 100;
107   status.discharging_time = std::numeric_limits<double>::infinity();
108   status.level = 0.5;
109   mock_battery_monitor()->DidChange(status);
110 
111   GURL test_url = GetTestUrl("battery_monitor",
112                              "battery_status_promise_resolution_test.html");
113   NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 2);
114   EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref());
115 }
116 
IN_PROC_BROWSER_TEST_F(BatteryMonitorTest,NavigatorGetBatteryListenChange)117 IN_PROC_BROWSER_TEST_F(BatteryMonitorTest, NavigatorGetBatteryListenChange) {
118   // From JavaScript request a promise for the battery status information.
119   // Once it resolves add an event listener for battery level change. Set
120   // battery level to 0.6 and invoke update. Check that the event listener
121   // is invoked with the correct value for level and navigate to #pass.
122   device::mojom::BatteryStatus status;
123   mock_battery_monitor()->DidChange(status);
124 
125   TestNavigationObserver same_tab_observer(shell()->web_contents(), 2);
126   GURL test_url =
127       GetTestUrl("battery_monitor", "battery_status_event_listener_test.html");
128   shell()->LoadURL(test_url);
129   same_tab_observer.Wait();
130   EXPECT_EQ("resolved", shell()->web_contents()->GetLastCommittedURL().ref());
131 
132   TestNavigationObserver same_tab_observer2(shell()->web_contents(), 1);
133   status.level = 0.6;
134   mock_battery_monitor()->DidChange(status);
135   same_tab_observer2.Wait();
136   EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref());
137 }
138 
139 }  //  namespace
140 
141 }  //  namespace content
142