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 "config.h"
6#include "bindings/core/v8/ScriptPromiseResolver.h"
7
8#include "bindings/core/v8/ScriptFunction.h"
9#include "bindings/core/v8/ScriptValue.h"
10#include "bindings/core/v8/V8Binding.h"
11#include "core/dom/DOMException.h"
12#include "core/dom/Document.h"
13#include "core/dom/ExceptionCode.h"
14#include "core/testing/DummyPageHolder.h"
15
16#include <gtest/gtest.h>
17#include <v8.h>
18
19namespace blink {
20
21namespace {
22
23void callback(const v8::FunctionCallbackInfo<v8::Value>& info) { }
24
25class Function : public ScriptFunction {
26public:
27    static v8::Handle<v8::Function> createFunction(ScriptState* scriptState, String* value)
28    {
29        Function* self = new Function(scriptState, value);
30        return self->bindToV8Function();
31    }
32
33private:
34    Function(ScriptState* scriptState, String* value)
35        : ScriptFunction(scriptState)
36        , m_value(value)
37    {
38    }
39
40    virtual ScriptValue call(ScriptValue value) OVERRIDE
41    {
42        ASSERT(!value.isEmpty());
43        *m_value = toCoreString(value.v8Value()->ToString());
44        return value;
45    }
46
47    String* m_value;
48};
49
50class ScriptPromiseResolverTest : public ::testing::Test {
51public:
52    ScriptPromiseResolverTest()
53        : m_pageHolder(DummyPageHolder::create())
54    {
55    }
56
57    virtual ~ScriptPromiseResolverTest()
58    {
59        ScriptState::Scope scope(scriptState());
60        // FIXME: We put this statement here to clear an exception from the
61        // isolate.
62        createClosure(callback, v8::Undefined(isolate()), isolate());
63
64        // Execute all pending microtasks
65        isolate()->RunMicrotasks();
66    }
67
68    OwnPtr<DummyPageHolder> m_pageHolder;
69    ScriptState* scriptState() const { return ScriptState::forMainWorld(&m_pageHolder->frame()); }
70    ExecutionContext* executionContext() const { return &m_pageHolder->document(); }
71    v8::Isolate* isolate() const { return scriptState()->isolate(); }
72};
73
74TEST_F(ScriptPromiseResolverTest, construct)
75{
76    ASSERT_FALSE(executionContext()->activeDOMObjectsAreStopped());
77    ScriptState::Scope scope(scriptState());
78    RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState());
79}
80
81TEST_F(ScriptPromiseResolverTest, resolve)
82{
83    RefPtr<ScriptPromiseResolver> resolver;
84    ScriptPromise promise;
85    {
86        ScriptState::Scope scope(scriptState());
87        resolver = ScriptPromiseResolver::create(scriptState());
88        promise = resolver->promise();
89    }
90
91    String onFulfilled, onRejected;
92    ASSERT_FALSE(promise.isEmpty());
93    {
94        ScriptState::Scope scope(scriptState());
95        promise.then(Function::createFunction(scriptState(), &onFulfilled), Function::createFunction(scriptState(), &onRejected));
96    }
97
98    EXPECT_EQ(String(), onFulfilled);
99    EXPECT_EQ(String(), onRejected);
100
101    isolate()->RunMicrotasks();
102
103    EXPECT_EQ(String(), onFulfilled);
104    EXPECT_EQ(String(), onRejected);
105
106    resolver->resolve("hello");
107
108    {
109        ScriptState::Scope scope(scriptState());
110        EXPECT_TRUE(resolver->promise().isEmpty());
111    }
112
113    EXPECT_EQ(String(), onFulfilled);
114    EXPECT_EQ(String(), onRejected);
115
116    isolate()->RunMicrotasks();
117
118    EXPECT_EQ("hello", onFulfilled);
119    EXPECT_EQ(String(), onRejected);
120
121    resolver->resolve("bye");
122    resolver->reject("bye");
123    isolate()->RunMicrotasks();
124
125    EXPECT_EQ("hello", onFulfilled);
126    EXPECT_EQ(String(), onRejected);
127}
128
129TEST_F(ScriptPromiseResolverTest, reject)
130{
131    RefPtr<ScriptPromiseResolver> resolver;
132    ScriptPromise promise;
133    {
134        ScriptState::Scope scope(scriptState());
135        resolver = ScriptPromiseResolver::create(scriptState());
136        promise = resolver->promise();
137    }
138
139    String onFulfilled, onRejected;
140    ASSERT_FALSE(promise.isEmpty());
141    {
142        ScriptState::Scope scope(scriptState());
143        promise.then(Function::createFunction(scriptState(), &onFulfilled), Function::createFunction(scriptState(), &onRejected));
144    }
145
146    EXPECT_EQ(String(), onFulfilled);
147    EXPECT_EQ(String(), onRejected);
148
149    isolate()->RunMicrotasks();
150
151    EXPECT_EQ(String(), onFulfilled);
152    EXPECT_EQ(String(), onRejected);
153
154    resolver->reject("hello");
155
156    {
157        ScriptState::Scope scope(scriptState());
158        EXPECT_TRUE(resolver->promise().isEmpty());
159    }
160
161    EXPECT_EQ(String(), onFulfilled);
162    EXPECT_EQ(String(), onRejected);
163
164    isolate()->RunMicrotasks();
165
166    EXPECT_EQ(String(), onFulfilled);
167    EXPECT_EQ("hello", onRejected);
168
169    resolver->resolve("bye");
170    resolver->reject("bye");
171    isolate()->RunMicrotasks();
172
173    EXPECT_EQ(String(), onFulfilled);
174    EXPECT_EQ("hello", onRejected);
175}
176
177TEST_F(ScriptPromiseResolverTest, stop)
178{
179    RefPtr<ScriptPromiseResolver> resolver;
180    ScriptPromise promise;
181    {
182        ScriptState::Scope scope(scriptState());
183        resolver = ScriptPromiseResolver::create(scriptState());
184        promise = resolver->promise();
185    }
186
187    String onFulfilled, onRejected;
188    ASSERT_FALSE(promise.isEmpty());
189    {
190        ScriptState::Scope scope(scriptState());
191        promise.then(Function::createFunction(scriptState(), &onFulfilled), Function::createFunction(scriptState(), &onRejected));
192    }
193
194    executionContext()->stopActiveDOMObjects();
195    {
196        ScriptState::Scope scope(scriptState());
197        EXPECT_TRUE(resolver->promise().isEmpty());
198    }
199
200    resolver->resolve("hello");
201    isolate()->RunMicrotasks();
202
203    EXPECT_EQ(String(), onFulfilled);
204    EXPECT_EQ(String(), onRejected);
205}
206
207TEST_F(ScriptPromiseResolverTest, keepAliveUntilResolved)
208{
209    RefPtr<ScriptPromiseResolver> resolver;
210    {
211        ScriptState::Scope scope(scriptState());
212        resolver = ScriptPromiseResolver::create(scriptState());
213    }
214    EXPECT_EQ(1, resolver->refCount());
215    resolver->keepAliveWhilePending();
216    EXPECT_EQ(2, resolver->refCount());
217
218    resolver->resolve("hello");
219    EXPECT_EQ(1, resolver->refCount());
220}
221
222TEST_F(ScriptPromiseResolverTest, keepAliveUntilRejected)
223{
224    RefPtr<ScriptPromiseResolver> resolver;
225    {
226        ScriptState::Scope scope(scriptState());
227        resolver = ScriptPromiseResolver::create(scriptState());
228    }
229    EXPECT_EQ(1, resolver->refCount());
230    resolver->keepAliveWhilePending();
231    EXPECT_EQ(2, resolver->refCount());
232
233    resolver->reject("hello");
234    EXPECT_EQ(1, resolver->refCount());
235}
236
237TEST_F(ScriptPromiseResolverTest, keepAliveUntilStopped)
238{
239    RefPtr<ScriptPromiseResolver> resolver;
240    {
241        ScriptState::Scope scope(scriptState());
242        resolver = ScriptPromiseResolver::create(scriptState());
243    }
244    EXPECT_EQ(1, resolver->refCount());
245    resolver->keepAliveWhilePending();
246    EXPECT_EQ(2, resolver->refCount());
247
248    executionContext()->stopActiveDOMObjects();
249    EXPECT_EQ(1, resolver->refCount());
250}
251
252TEST_F(ScriptPromiseResolverTest, suspend)
253{
254    RefPtr<ScriptPromiseResolver> resolver;
255    {
256        ScriptState::Scope scope(scriptState());
257        resolver = ScriptPromiseResolver::create(scriptState());
258    }
259    EXPECT_EQ(1, resolver->refCount());
260    resolver->keepAliveWhilePending();
261    EXPECT_EQ(2, resolver->refCount());
262    executionContext()->suspendActiveDOMObjects();
263    resolver->resolve("hello");
264    EXPECT_EQ(3, resolver->refCount());
265
266    executionContext()->stopActiveDOMObjects();
267    EXPECT_EQ(1, resolver->refCount());
268}
269
270TEST_F(ScriptPromiseResolverTest, resolveVoid)
271{
272    RefPtr<ScriptPromiseResolver> resolver;
273    ScriptPromise promise;
274    {
275        ScriptState::Scope scope(scriptState());
276        resolver = ScriptPromiseResolver::create(scriptState());
277        promise = resolver->promise();
278    }
279
280    String onFulfilled, onRejected;
281    ASSERT_FALSE(promise.isEmpty());
282    {
283        ScriptState::Scope scope(scriptState());
284        promise.then(Function::createFunction(scriptState(), &onFulfilled), Function::createFunction(scriptState(), &onRejected));
285    }
286
287    resolver->resolve();
288    isolate()->RunMicrotasks();
289
290    EXPECT_EQ("undefined", onFulfilled);
291    EXPECT_EQ(String(), onRejected);
292}
293
294TEST_F(ScriptPromiseResolverTest, rejectVoid)
295{
296    RefPtr<ScriptPromiseResolver> resolver;
297    ScriptPromise promise;
298    {
299        ScriptState::Scope scope(scriptState());
300        resolver = ScriptPromiseResolver::create(scriptState());
301        promise = resolver->promise();
302    }
303
304    String onFulfilled, onRejected;
305    ASSERT_FALSE(promise.isEmpty());
306    {
307        ScriptState::Scope scope(scriptState());
308        promise.then(Function::createFunction(scriptState(), &onFulfilled), Function::createFunction(scriptState(), &onRejected));
309    }
310
311    resolver->reject();
312    isolate()->RunMicrotasks();
313
314    EXPECT_EQ(String(), onFulfilled);
315    EXPECT_EQ("undefined", onRejected);
316}
317
318} // namespace
319
320} // namespace blink
321