1/*
2 * Copyright (C) 2014 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "bindings/core/v8/ScriptPromise.h"
33
34#include "bindings/core/v8/ScriptFunction.h"
35#include "bindings/core/v8/ScriptValue.h"
36#include "bindings/core/v8/V8Binding.h"
37#include "core/dom/DOMException.h"
38#include "core/dom/ExceptionCode.h"
39
40#include <gtest/gtest.h>
41#include <v8.h>
42
43namespace blink {
44
45namespace {
46
47void callback(const v8::FunctionCallbackInfo<v8::Value>& info) { }
48
49class Function : public ScriptFunction {
50public:
51    static v8::Handle<v8::Function> createFunction(ScriptState* scriptState, String* value)
52    {
53        Function* self = new Function(scriptState, value);
54        return self->bindToV8Function();
55    }
56
57private:
58    Function(ScriptState* scriptState, String* value)
59        : ScriptFunction(scriptState)
60        , m_value(value)
61    {
62    }
63
64    virtual ScriptValue call(ScriptValue value) OVERRIDE
65    {
66        ASSERT(!value.isEmpty());
67        *m_value = toCoreString(value.v8Value()->ToString());
68        return value;
69    }
70
71    String* m_value;
72};
73
74class ScriptPromiseTest : public testing::Test {
75public:
76    ScriptPromiseTest()
77        : m_scope(v8::Isolate::GetCurrent())
78    {
79    }
80
81    ~ScriptPromiseTest()
82    {
83        // FIXME: We put this statement here to clear an exception from the isolate.
84        createClosure(callback, v8::Undefined(m_scope.isolate()), m_scope.isolate());
85
86        // Execute all pending microtasks
87        isolate()->RunMicrotasks();
88    }
89
90    ScriptState* scriptState() const { return m_scope.scriptState(); }
91    v8::Isolate* isolate() const { return m_scope.isolate(); }
92
93protected:
94    typedef ScriptPromise::InternalResolver Resolver;
95    V8TestingScope m_scope;
96};
97
98TEST_F(ScriptPromiseTest, constructFromNonPromise)
99{
100    v8::TryCatch trycatch;
101    ScriptPromise promise(scriptState(), v8::Undefined(isolate()));
102    ASSERT_TRUE(trycatch.HasCaught());
103    ASSERT_TRUE(promise.isEmpty());
104}
105
106TEST_F(ScriptPromiseTest, thenResolve)
107{
108    Resolver resolver(scriptState());
109    ScriptPromise promise = resolver.promise();
110    String onFulfilled, onRejected;
111    promise.then(Function::createFunction(scriptState(), &onFulfilled), Function::createFunction(scriptState(), &onRejected));
112
113    ASSERT_FALSE(promise.isEmpty());
114    EXPECT_EQ(String(), onFulfilled);
115    EXPECT_EQ(String(), onRejected);
116
117    isolate()->RunMicrotasks();
118    resolver.resolve(v8String(isolate(), "hello"));
119
120    EXPECT_EQ(String(), onFulfilled);
121    EXPECT_EQ(String(), onRejected);
122
123    isolate()->RunMicrotasks();
124
125    EXPECT_EQ("hello", onFulfilled);
126    EXPECT_EQ(String(), onRejected);
127}
128
129TEST_F(ScriptPromiseTest, resolveThen)
130{
131    Resolver resolver(scriptState());
132    ScriptPromise promise = resolver.promise();
133    String onFulfilled, onRejected;
134    resolver.resolve(v8String(isolate(), "hello"));
135    promise.then(Function::createFunction(scriptState(), &onFulfilled), Function::createFunction(scriptState(), &onRejected));
136
137    ASSERT_FALSE(promise.isEmpty());
138    EXPECT_EQ(String(), onFulfilled);
139    EXPECT_EQ(String(), onRejected);
140
141    isolate()->RunMicrotasks();
142
143    EXPECT_EQ("hello", onFulfilled);
144    EXPECT_EQ(String(), onRejected);
145}
146
147TEST_F(ScriptPromiseTest, thenReject)
148{
149    Resolver resolver(scriptState());
150    ScriptPromise promise = resolver.promise();
151    String onFulfilled, onRejected;
152    promise.then(Function::createFunction(scriptState(), &onFulfilled), Function::createFunction(scriptState(), &onRejected));
153
154    ASSERT_FALSE(promise.isEmpty());
155    EXPECT_EQ(String(), onFulfilled);
156    EXPECT_EQ(String(), onRejected);
157
158    isolate()->RunMicrotasks();
159    resolver.reject(v8String(isolate(), "hello"));
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
170TEST_F(ScriptPromiseTest, rejectThen)
171{
172    Resolver resolver(scriptState());
173    ScriptPromise promise = resolver.promise();
174    String onFulfilled, onRejected;
175    resolver.reject(v8String(isolate(), "hello"));
176    promise.then(Function::createFunction(scriptState(), &onFulfilled), Function::createFunction(scriptState(), &onRejected));
177
178    ASSERT_FALSE(promise.isEmpty());
179    EXPECT_EQ(String(), onFulfilled);
180    EXPECT_EQ(String(), onRejected);
181
182    isolate()->RunMicrotasks();
183
184    EXPECT_EQ(String(), onFulfilled);
185    EXPECT_EQ("hello", onRejected);
186}
187
188TEST_F(ScriptPromiseTest, castPromise)
189{
190    ScriptPromise promise = Resolver(scriptState()).promise();
191    ScriptPromise newPromise = ScriptPromise::cast(scriptState(), promise.v8Value());
192
193    ASSERT_FALSE(promise.isEmpty());
194    EXPECT_EQ(promise.v8Value(), newPromise.v8Value());
195}
196
197TEST_F(ScriptPromiseTest, castNonPromise)
198{
199    String onFulfilled1, onFulfilled2, onRejected1, onRejected2;
200
201    ScriptValue value = ScriptValue(scriptState(), v8String(isolate(), "hello"));
202    ScriptPromise promise1 = ScriptPromise::cast(scriptState(), ScriptValue(value));
203    ScriptPromise promise2 = ScriptPromise::cast(scriptState(), ScriptValue(value));
204    promise1.then(Function::createFunction(scriptState(), &onFulfilled1), Function::createFunction(scriptState(), &onRejected1));
205    promise2.then(Function::createFunction(scriptState(), &onFulfilled2), Function::createFunction(scriptState(), &onRejected2));
206
207    ASSERT_FALSE(promise1.isEmpty());
208    ASSERT_FALSE(promise2.isEmpty());
209    EXPECT_NE(promise1.v8Value(), promise2.v8Value());
210
211    ASSERT_TRUE(promise1.v8Value()->IsPromise());
212    ASSERT_TRUE(promise2.v8Value()->IsPromise());
213
214    EXPECT_EQ(String(), onFulfilled1);
215    EXPECT_EQ(String(), onFulfilled2);
216    EXPECT_EQ(String(), onRejected1);
217    EXPECT_EQ(String(), onRejected2);
218
219    isolate()->RunMicrotasks();
220
221    EXPECT_EQ("hello", onFulfilled1);
222    EXPECT_EQ("hello", onFulfilled2);
223    EXPECT_EQ(String(), onRejected1);
224    EXPECT_EQ(String(), onRejected2);
225}
226
227TEST_F(ScriptPromiseTest, reject)
228{
229    String onFulfilled, onRejected;
230
231    ScriptValue value = ScriptValue(scriptState(), v8String(isolate(), "hello"));
232    ScriptPromise promise = ScriptPromise::reject(scriptState(), ScriptValue(value));
233    promise.then(Function::createFunction(scriptState(), &onFulfilled), Function::createFunction(scriptState(), &onRejected));
234
235    ASSERT_FALSE(promise.isEmpty());
236    ASSERT_TRUE(promise.v8Value()->IsPromise());
237
238    EXPECT_EQ(String(), onFulfilled);
239    EXPECT_EQ(String(), onRejected);
240
241    isolate()->RunMicrotasks();
242
243    EXPECT_EQ(String(), onFulfilled);
244    EXPECT_EQ("hello", onRejected);
245}
246
247TEST_F(ScriptPromiseTest, rejectWithExceptionState)
248{
249    String onFulfilled, onRejected;
250    ScriptPromise promise = ScriptPromise::rejectWithDOMException(scriptState(), DOMException::create(SyntaxError, "some syntax error"));
251    promise.then(Function::createFunction(scriptState(), &onFulfilled), Function::createFunction(scriptState(), &onRejected));
252
253    ASSERT_FALSE(promise.isEmpty());
254    EXPECT_EQ(String(), onFulfilled);
255    EXPECT_EQ(String(), onRejected);
256
257    isolate()->RunMicrotasks();
258
259    EXPECT_EQ(String(), onFulfilled);
260    EXPECT_EQ("SyntaxError: some syntax error", onRejected);
261}
262
263} // namespace
264
265} // namespace blink
266