1// Copyright 2014 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 "base/command_line.h"
6#include "base/synchronization/waitable_event.h"
7#include "base/threading/platform_thread.h"
8#include "content/browser/device_sensors/data_fetcher_shared_memory.h"
9#include "content/browser/device_sensors/device_inertial_sensor_service.h"
10#include "content/common/device_sensors/device_light_hardware_buffer.h"
11#include "content/common/device_sensors/device_motion_hardware_buffer.h"
12#include "content/common/device_sensors/device_orientation_hardware_buffer.h"
13#include "content/public/browser/browser_thread.h"
14#include "content/public/browser/web_contents.h"
15#include "content/public/common/content_switches.h"
16#include "content/public/test/content_browser_test.h"
17#include "content/public/test/content_browser_test_utils.h"
18#include "content/public/test/test_navigation_observer.h"
19#include "content/public/test/test_utils.h"
20#include "content/shell/browser/shell.h"
21#include "content/shell/browser/shell_javascript_dialog_manager.h"
22
23namespace content {
24
25namespace {
26
27class FakeDataFetcher : public DataFetcherSharedMemory {
28 public:
29  FakeDataFetcher()
30      : started_orientation_(false, false),
31        stopped_orientation_(false, false),
32        started_motion_(false, false),
33        stopped_motion_(false, false),
34        started_light_(false, false),
35        stopped_light_(false, false),
36        sensor_data_available_(true) {}
37  virtual ~FakeDataFetcher() { }
38
39  virtual bool Start(ConsumerType consumer_type, void* buffer) OVERRIDE {
40    EXPECT_TRUE(buffer);
41
42    switch (consumer_type) {
43      case CONSUMER_TYPE_MOTION:
44        {
45          DeviceMotionHardwareBuffer* motion_buffer =
46              static_cast<DeviceMotionHardwareBuffer*>(buffer);
47          if (sensor_data_available_)
48            UpdateMotion(motion_buffer);
49          SetMotionBufferReady(motion_buffer);
50          started_motion_.Signal();
51        }
52        break;
53      case CONSUMER_TYPE_ORIENTATION:
54        {
55          DeviceOrientationHardwareBuffer* orientation_buffer =
56              static_cast<DeviceOrientationHardwareBuffer*>(buffer);
57          if (sensor_data_available_)
58            UpdateOrientation(orientation_buffer);
59          SetOrientationBufferReady(orientation_buffer);
60          started_orientation_.Signal();
61        }
62        break;
63      case CONSUMER_TYPE_LIGHT:
64        {
65          DeviceLightHardwareBuffer* light_buffer =
66              static_cast<DeviceLightHardwareBuffer*>(buffer);
67          UpdateLight(light_buffer,
68                      sensor_data_available_
69                          ? 100
70                          : std::numeric_limits<double>::infinity());
71          started_light_.Signal();
72        }
73        break;
74      default:
75        return false;
76    }
77    return true;
78  }
79
80  virtual bool Stop(ConsumerType consumer_type) OVERRIDE {
81    switch (consumer_type) {
82      case CONSUMER_TYPE_MOTION:
83        stopped_motion_.Signal();
84        break;
85      case CONSUMER_TYPE_ORIENTATION:
86        stopped_orientation_.Signal();
87        break;
88      case CONSUMER_TYPE_LIGHT:
89        stopped_light_.Signal();
90        break;
91      default:
92        return false;
93    }
94    return true;
95  }
96
97  virtual void Fetch(unsigned consumer_bitmask) OVERRIDE {
98    FAIL() << "fetch should not be called";
99  }
100
101  virtual FetcherType GetType() const OVERRIDE {
102    return FETCHER_TYPE_DEFAULT;
103  }
104
105  void SetSensorDataAvailable(bool available) {
106    sensor_data_available_ = available;
107  }
108
109  void SetMotionBufferReady(DeviceMotionHardwareBuffer* buffer) {
110    buffer->seqlock.WriteBegin();
111    buffer->data.allAvailableSensorsAreActive = true;
112    buffer->seqlock.WriteEnd();
113  }
114
115  void SetOrientationBufferReady(DeviceOrientationHardwareBuffer* buffer) {
116    buffer->seqlock.WriteBegin();
117    buffer->data.allAvailableSensorsAreActive = true;
118    buffer->seqlock.WriteEnd();
119  }
120
121  void UpdateMotion(DeviceMotionHardwareBuffer* buffer) {
122    buffer->seqlock.WriteBegin();
123    buffer->data.accelerationX = 1;
124    buffer->data.hasAccelerationX = true;
125    buffer->data.accelerationY = 2;
126    buffer->data.hasAccelerationY = true;
127    buffer->data.accelerationZ = 3;
128    buffer->data.hasAccelerationZ = true;
129
130    buffer->data.accelerationIncludingGravityX = 4;
131    buffer->data.hasAccelerationIncludingGravityX = true;
132    buffer->data.accelerationIncludingGravityY = 5;
133    buffer->data.hasAccelerationIncludingGravityY = true;
134    buffer->data.accelerationIncludingGravityZ = 6;
135    buffer->data.hasAccelerationIncludingGravityZ = true;
136
137    buffer->data.rotationRateAlpha = 7;
138    buffer->data.hasRotationRateAlpha = true;
139    buffer->data.rotationRateBeta = 8;
140    buffer->data.hasRotationRateBeta = true;
141    buffer->data.rotationRateGamma = 9;
142    buffer->data.hasRotationRateGamma = true;
143
144    buffer->data.interval = 100;
145    buffer->data.allAvailableSensorsAreActive = true;
146    buffer->seqlock.WriteEnd();
147  }
148
149  void UpdateOrientation(DeviceOrientationHardwareBuffer* buffer) {
150    buffer->seqlock.WriteBegin();
151    buffer->data.alpha = 1;
152    buffer->data.hasAlpha = true;
153    buffer->data.beta = 2;
154    buffer->data.hasBeta = true;
155    buffer->data.gamma = 3;
156    buffer->data.hasGamma = true;
157    buffer->data.allAvailableSensorsAreActive = true;
158    buffer->seqlock.WriteEnd();
159  }
160
161  void UpdateLight(DeviceLightHardwareBuffer* buffer, double lux) {
162    buffer->seqlock.WriteBegin();
163    buffer->data.value = lux;
164    buffer->seqlock.WriteEnd();
165  }
166
167  base::WaitableEvent started_orientation_;
168  base::WaitableEvent stopped_orientation_;
169  base::WaitableEvent started_motion_;
170  base::WaitableEvent stopped_motion_;
171  base::WaitableEvent started_light_;
172  base::WaitableEvent stopped_light_;
173  bool sensor_data_available_;
174
175 private:
176  DISALLOW_COPY_AND_ASSIGN(FakeDataFetcher);
177};
178
179
180class DeviceInertialSensorBrowserTest : public ContentBrowserTest  {
181 public:
182  DeviceInertialSensorBrowserTest()
183      : fetcher_(NULL),
184        io_loop_finished_event_(false, false) {
185  }
186
187  virtual void SetUpOnMainThread() OVERRIDE {
188    BrowserThread::PostTask(
189        BrowserThread::IO, FROM_HERE,
190        base::Bind(&DeviceInertialSensorBrowserTest::SetUpOnIOThread, this));
191    io_loop_finished_event_.Wait();
192  }
193
194  void SetUpOnIOThread() {
195    fetcher_ = new FakeDataFetcher();
196    DeviceInertialSensorService::GetInstance()->
197        SetDataFetcherForTesting(fetcher_);
198    io_loop_finished_event_.Signal();
199  }
200
201  void DelayAndQuit(base::TimeDelta delay) {
202    base::PlatformThread::Sleep(delay);
203    base::MessageLoop::current()->QuitWhenIdle();
204  }
205
206  void WaitForAlertDialogAndQuitAfterDelay(base::TimeDelta delay) {
207    ShellJavaScriptDialogManager* dialog_manager=
208        static_cast<ShellJavaScriptDialogManager*>(
209            shell()->GetJavaScriptDialogManager());
210
211    scoped_refptr<MessageLoopRunner> runner = new MessageLoopRunner();
212    dialog_manager->set_dialog_request_callback(
213        base::Bind(&DeviceInertialSensorBrowserTest::DelayAndQuit, this,
214            delay));
215    runner->Run();
216  }
217
218  FakeDataFetcher* fetcher_;
219
220 private:
221  base::WaitableEvent io_loop_finished_event_;
222};
223
224IN_PROC_BROWSER_TEST_F(DeviceInertialSensorBrowserTest, OrientationTest) {
225  // The test page will register an event handler for orientation events,
226  // expects to get an event with fake values, then removes the event
227  // handler and navigates to #pass.
228  GURL test_url = GetTestUrl("device_sensors", "device_orientation_test.html");
229  NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 2);
230
231  EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref());
232  fetcher_->started_orientation_.Wait();
233  fetcher_->stopped_orientation_.Wait();
234}
235
236IN_PROC_BROWSER_TEST_F(DeviceInertialSensorBrowserTest, LightTest) {
237  // The test page will register an event handler for light events,
238  // expects to get an event with fake values, then removes the event
239  // handler and navigates to #pass.
240  GURL test_url = GetTestUrl("device_sensors", "device_light_test.html");
241
242  // TODO(riju): remove command line args when the feature goes stable.
243  if (!CommandLine::ForCurrentProcess()->HasSwitch(
244      switches::kEnableExperimentalWebPlatformFeatures)) {
245    CommandLine::ForCurrentProcess()->AppendSwitch(
246        switches::kEnableExperimentalWebPlatformFeatures);
247  }
248
249  NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 2);
250
251  EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref());
252  fetcher_->started_light_.Wait();
253  fetcher_->stopped_light_.Wait();
254}
255
256IN_PROC_BROWSER_TEST_F(DeviceInertialSensorBrowserTest, MotionTest) {
257  // The test page will register an event handler for motion events,
258  // expects to get an event with fake values, then removes the event
259  // handler and navigates to #pass.
260  GURL test_url = GetTestUrl("device_sensors", "device_motion_test.html");
261  NavigateToURLBlockUntilNavigationsComplete(shell(), test_url, 2);
262
263  EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref());
264  fetcher_->started_motion_.Wait();
265  fetcher_->stopped_motion_.Wait();
266}
267
268// crbug/416406. The test is flaky.
269IN_PROC_BROWSER_TEST_F(DeviceInertialSensorBrowserTest,
270                       DISABLED_LightOneOffInfintyTest) {
271  // The test page will register an event handler for light events,
272  // expects to get an event with value equal to Infinity. This tests that the
273  // one-off infinity event still propagates to window after the alert is
274  // dismissed and the callback is invoked which navigates to #pass.
275  fetcher_->SetSensorDataAvailable(false);
276
277  // TODO(riju): remove command line args when the feature goes stable.
278  if (!CommandLine::ForCurrentProcess()->HasSwitch(
279      switches::kEnableExperimentalWebPlatformFeatures)) {
280    CommandLine::ForCurrentProcess()->AppendSwitch(
281        switches::kEnableExperimentalWebPlatformFeatures);
282  }
283
284  TestNavigationObserver same_tab_observer(shell()->web_contents(), 2);
285
286  GURL test_url =
287      GetTestUrl("device_sensors", "device_light_infinity_test.html");
288  shell()->LoadURL(test_url);
289
290  WaitForAlertDialogAndQuitAfterDelay(base::TimeDelta::FromMilliseconds(1000));
291
292  fetcher_->started_light_.Wait();
293  fetcher_->stopped_light_.Wait();
294  same_tab_observer.Wait();
295  EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref());
296}
297
298// Flaking in the android try bot. See http://crbug.com/360578.
299#if defined(OS_ANDROID)
300#define MAYBE_OrientationNullTestWithAlert DISABLED_OrientationNullTestWithAlert
301#else
302#define MAYBE_OrientationNullTestWithAlert OrientationNullTestWithAlert
303#endif
304IN_PROC_BROWSER_TEST_F(DeviceInertialSensorBrowserTest,
305    MAYBE_OrientationNullTestWithAlert) {
306  // The test page will register an event handler for orientation events,
307  // expects to get an event with null values. The test raises a modal alert
308  // dialog with a delay to test that the one-off null-event still propagates
309  // to window after the alert is dismissed and the callback is invoked which
310  // navigates to #pass.
311  fetcher_->SetSensorDataAvailable(false);
312  TestNavigationObserver same_tab_observer(shell()->web_contents(), 2);
313
314  GURL test_url = GetTestUrl("device_sensors",
315                             "device_orientation_null_test_with_alert.html");
316  shell()->LoadURL(test_url);
317
318  // TODO(timvolodine): investigate if it is possible to test this without
319  // delay, crbug.com/360044.
320  WaitForAlertDialogAndQuitAfterDelay(base::TimeDelta::FromMilliseconds(1000));
321
322  fetcher_->started_orientation_.Wait();
323  fetcher_->stopped_orientation_.Wait();
324  same_tab_observer.Wait();
325  EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref());
326}
327
328// Flaking in the android try bot. See http://crbug.com/360578.
329#if defined(OS_ANDROID)
330#define MAYBE_MotionNullTestWithAlert DISABLED_MotionNullTestWithAlert
331#else
332#define MAYBE_MotionNullTestWithAlert MotionNullTestWithAlert
333#endif
334IN_PROC_BROWSER_TEST_F(DeviceInertialSensorBrowserTest,
335    MAYBE_MotionNullTestWithAlert) {
336  // The test page will register an event handler for motion events,
337  // expects to get an event with null values. The test raises a modal alert
338  // dialog with a delay to test that the one-off null-event still propagates
339  // to window after the alert is dismissed and the callback is invoked which
340  // navigates to #pass.
341  fetcher_->SetSensorDataAvailable(false);
342  TestNavigationObserver same_tab_observer(shell()->web_contents(), 2);
343
344  GURL test_url =
345      GetTestUrl("device_sensors", "device_motion_null_test_with_alert.html");
346  shell()->LoadURL(test_url);
347
348  // TODO(timvolodine): investigate if it is possible to test this without
349  // delay, crbug.com/360044.
350  WaitForAlertDialogAndQuitAfterDelay(base::TimeDelta::FromMilliseconds(1000));
351
352  fetcher_->started_motion_.Wait();
353  fetcher_->stopped_motion_.Wait();
354  same_tab_observer.Wait();
355  EXPECT_EQ("pass", shell()->web_contents()->GetLastCommittedURL().ref());
356}
357
358}  //  namespace
359
360}  //  namespace content
361