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 "screen_orientation_dispatcher.h"
6
7#include <list>
8
9#include "base/logging.h"
10#include "base/memory/scoped_ptr.h"
11#include "content/common/screen_orientation_messages.h"
12#include "content/public/test/test_utils.h"
13#include "ipc/ipc_test_sink.h"
14#include "testing/gtest/include/gtest/gtest.h"
15#include "third_party/WebKit/public/platform/WebLockOrientationCallback.h"
16
17namespace content {
18
19// MockLockOrientationCallback is an implementation of
20// WebLockOrientationCallback and takes a LockOrientationResultHolder* as a
21// parameter when being constructed. The |results_| pointer is owned by the
22// caller and not by the callback object. The intent being that as soon as the
23// callback is resolved, it will be killed so we use the
24// LockOrientationResultHolder to know in which state the callback object is at
25// any time.
26class MockLockOrientationCallback :
27    public blink::WebLockOrientationCallback {
28 public:
29  struct LockOrientationResultHolder {
30    LockOrientationResultHolder()
31        : succeeded_(false), failed_(false) {}
32
33    bool succeeded_;
34    bool failed_;
35    blink::WebLockOrientationError error_;
36  };
37
38  explicit MockLockOrientationCallback(LockOrientationResultHolder* results)
39      : results_(results) {}
40
41  virtual void onSuccess() {
42    results_->succeeded_ = true;
43  }
44
45  virtual void onError(blink::WebLockOrientationError error) {
46    results_->failed_ = true;
47    results_->error_ = error;
48  }
49
50 private:
51  virtual ~MockLockOrientationCallback() {}
52
53  LockOrientationResultHolder* results_;
54};
55
56class ScreenOrientationDispatcherWithSink : public ScreenOrientationDispatcher {
57 public:
58  explicit ScreenOrientationDispatcherWithSink(IPC::TestSink* sink)
59      :ScreenOrientationDispatcher(NULL) , sink_(sink) {
60  }
61
62  virtual bool Send(IPC::Message* message) OVERRIDE {
63    return sink_->Send(message);
64  }
65
66  IPC::TestSink* sink_;
67};
68
69class ScreenOrientationDispatcherTest : public testing::Test {
70 protected:
71  virtual void SetUp() OVERRIDE {
72    dispatcher_.reset(new ScreenOrientationDispatcherWithSink(&sink_));
73  }
74
75  int GetFirstLockRequestIdFromSink() {
76    const IPC::Message* msg = sink().GetFirstMessageMatching(
77        ScreenOrientationHostMsg_LockRequest::ID);
78    EXPECT_TRUE(msg != NULL);
79
80    Tuple2<blink::WebScreenOrientationLockType,int> params;
81    ScreenOrientationHostMsg_LockRequest::Read(msg, &params);
82    return params.b;
83  }
84
85  IPC::TestSink& sink() {
86    return sink_;
87  }
88
89  void LockOrientation(blink::WebScreenOrientationLockType orientation,
90                       blink::WebLockOrientationCallback* callback) {
91    dispatcher_->lockOrientation(orientation, callback);
92  }
93
94  void UnlockOrientation() {
95    dispatcher_->unlockOrientation();
96  }
97
98  void OnMessageReceived(const IPC::Message& message) {
99    dispatcher_->OnMessageReceived(message);
100  }
101
102  int routing_id() const {
103    // We return a fake routing_id() in the context of this test.
104    return 0;
105  }
106
107  IPC::TestSink sink_;
108  scoped_ptr<ScreenOrientationDispatcher> dispatcher_;
109};
110
111// Test that calling lockOrientation() followed by unlockOrientation() cancel
112// the lockOrientation().
113TEST_F(ScreenOrientationDispatcherTest, CancelPending_Unlocking) {
114  MockLockOrientationCallback::LockOrientationResultHolder callback_results;
115  LockOrientation(blink::WebScreenOrientationLockPortraitPrimary,
116                  new MockLockOrientationCallback(&callback_results));
117  UnlockOrientation();
118
119  EXPECT_FALSE(callback_results.succeeded_);
120  EXPECT_TRUE(callback_results.failed_);
121  EXPECT_EQ(blink::WebLockOrientationErrorCanceled, callback_results.error_);
122}
123
124// Test that calling lockOrientation() twice cancel the first lockOrientation().
125TEST_F(ScreenOrientationDispatcherTest, CancelPending_DoubleLock) {
126  MockLockOrientationCallback::LockOrientationResultHolder callback_results;
127  // We create the object to prevent leaks but never actually use it.
128  MockLockOrientationCallback::LockOrientationResultHolder callback_results2;
129
130  LockOrientation(blink::WebScreenOrientationLockPortraitPrimary,
131                  new MockLockOrientationCallback(&callback_results));
132  LockOrientation(blink::WebScreenOrientationLockPortraitPrimary,
133                  new MockLockOrientationCallback(&callback_results2));
134
135  EXPECT_FALSE(callback_results.succeeded_);
136  EXPECT_TRUE(callback_results.failed_);
137  EXPECT_EQ(blink::WebLockOrientationErrorCanceled, callback_results.error_);
138}
139
140// Test that when a LockError message is received, the request is set as failed
141// with the correct values.
142TEST_F(ScreenOrientationDispatcherTest, LockRequest_Error) {
143  std::list<blink::WebLockOrientationError> errors;
144  errors.push_back(blink::WebLockOrientationErrorNotAvailable);
145  errors.push_back(
146      blink::WebLockOrientationErrorFullScreenRequired);
147  errors.push_back(blink::WebLockOrientationErrorCanceled);
148
149  for (std::list<blink::WebLockOrientationError>::const_iterator
150          it = errors.begin(); it != errors.end(); ++it) {
151    MockLockOrientationCallback::LockOrientationResultHolder callback_results;
152    LockOrientation(blink::WebScreenOrientationLockPortraitPrimary,
153                    new MockLockOrientationCallback(&callback_results));
154
155    int request_id = GetFirstLockRequestIdFromSink();
156    OnMessageReceived(
157        ScreenOrientationMsg_LockError(routing_id(), request_id, *it));
158
159    EXPECT_FALSE(callback_results.succeeded_);
160    EXPECT_TRUE(callback_results.failed_);
161    EXPECT_EQ(*it, callback_results.error_);
162
163    sink().ClearMessages();
164  }
165}
166
167// Test that when a LockSuccess message is received, the request is set as
168// succeeded.
169TEST_F(ScreenOrientationDispatcherTest, LockRequest_Success) {
170  MockLockOrientationCallback::LockOrientationResultHolder callback_results;
171  LockOrientation(blink::WebScreenOrientationLockPortraitPrimary,
172                  new MockLockOrientationCallback(&callback_results));
173
174  int request_id = GetFirstLockRequestIdFromSink();
175  OnMessageReceived(ScreenOrientationMsg_LockSuccess(routing_id(),
176                                                     request_id));
177
178  EXPECT_TRUE(callback_results.succeeded_);
179  EXPECT_FALSE(callback_results.failed_);
180
181  sink().ClearMessages();
182}
183
184// Test an edge case: a LockSuccess is received but it matches no pending
185// callback.
186TEST_F(ScreenOrientationDispatcherTest, SuccessForUnknownRequest) {
187  MockLockOrientationCallback::LockOrientationResultHolder callback_results;
188  LockOrientation(blink::WebScreenOrientationLockPortraitPrimary,
189                  new MockLockOrientationCallback(&callback_results));
190
191  int request_id = GetFirstLockRequestIdFromSink();
192  OnMessageReceived(ScreenOrientationMsg_LockSuccess(routing_id(),
193                                                     request_id + 1));
194
195  EXPECT_FALSE(callback_results.succeeded_);
196  EXPECT_FALSE(callback_results.failed_);
197}
198
199// Test an edge case: a LockError is received but it matches no pending
200// callback.
201TEST_F(ScreenOrientationDispatcherTest, ErrorForUnknownRequest) {
202  MockLockOrientationCallback::LockOrientationResultHolder callback_results;
203  LockOrientation(blink::WebScreenOrientationLockPortraitPrimary,
204                  new MockLockOrientationCallback(&callback_results));
205
206  int request_id = GetFirstLockRequestIdFromSink();
207  OnMessageReceived(ScreenOrientationMsg_LockError(
208      routing_id(), request_id + 1, blink::WebLockOrientationErrorCanceled));
209
210  EXPECT_FALSE(callback_results.succeeded_);
211  EXPECT_FALSE(callback_results.failed_);
212}
213
214// Test the following scenario:
215// - request1 is received by the dispatcher;
216// - request2 is received by the dispatcher;
217// - request1 is rejected;
218// - request1 success response is received.
219// Expected: request1 is still rejected, request2 has not been set as succeeded.
220TEST_F(ScreenOrientationDispatcherTest, RaceScenario) {
221  MockLockOrientationCallback::LockOrientationResultHolder callback_results1;
222  MockLockOrientationCallback::LockOrientationResultHolder callback_results2;
223
224  LockOrientation(blink::WebScreenOrientationLockPortraitPrimary,
225                  new MockLockOrientationCallback(&callback_results1));
226  int request_id1 = GetFirstLockRequestIdFromSink();
227
228  LockOrientation(blink::WebScreenOrientationLockLandscapePrimary,
229                  new MockLockOrientationCallback(&callback_results2));
230
231  // callback_results1 must be rejected, tested in CancelPending_DoubleLock.
232
233  OnMessageReceived(ScreenOrientationMsg_LockSuccess(routing_id(),
234                                                     request_id1));
235
236  // First request is still rejected.
237  EXPECT_FALSE(callback_results1.succeeded_);
238  EXPECT_TRUE(callback_results1.failed_);
239  EXPECT_EQ(blink::WebLockOrientationErrorCanceled, callback_results1.error_);
240
241  // Second request is still pending.
242  EXPECT_FALSE(callback_results2.succeeded_);
243  EXPECT_FALSE(callback_results2.failed_);
244}
245
246}  // namespace content
247