1/*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecmascript/jobs/micro_job_queue.h"
17
18#include "ecmascript/ecma_context.h"
19#include "ecmascript/jobs/pending_job.h"
20#include "ecmascript/global_env.h"
21#include "ecmascript/js_promise.h"
22#include "ecmascript/tagged_queue.h"
23#include "ecmascript/tests/test_helper.h"
24
25using namespace panda::ecmascript;
26
27namespace panda::test {
28using MicroJobQueue = ecmascript::job::MicroJobQueue;
29using PendingJob = ecmascript::job::PendingJob;
30using QueueType = job::QueueType;
31class MicroJobQueueTest : public testing::Test {
32public:
33    static void SetUpTestCase()
34    {
35        GTEST_LOG_(INFO) << "SetUpTestCase";
36    }
37
38    static void TearDownTestCase()
39    {
40        GTEST_LOG_(INFO) << "TearDownCase";
41    }
42
43    void SetUp() override
44    {
45        TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
46    }
47
48    void TearDown() override
49    {
50        TestHelper::DestroyEcmaVMWithScope(instance, scope);
51    }
52
53    EcmaVM *instance {nullptr};
54    EcmaHandleScope *scope {nullptr};
55    JSThread *thread {nullptr};
56};
57
58/**
59 * @tc.name: GetJobQueue
60 * @tc.desc: Check whether the result returned through "GetPromiseJobQueue" and "GetScriptJobQueue" function
61 *           is within expectations.
62 * @tc.type: FUNC
63 * @tc.require:
64 */
65HWTEST_F_L0(MicroJobQueueTest, GetJobQueue)
66{
67    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
68    uint32_t capacity = 4;
69    JSHandle<JSTaggedValue> handleValue(thread, JSTaggedValue(123));
70
71    JSHandle<TaggedQueue> handlePromiseQueue = factory->NewTaggedQueue(capacity);
72    TaggedQueue::PushFixedQueue(thread, handlePromiseQueue, handleValue);
73    JSHandle<TaggedQueue> handleScriptQueue = factory->NewTaggedQueue(capacity - 1);
74
75    JSHandle<MicroJobQueue> handleMicroJobQueue = factory->NewMicroJobQueue();
76    EXPECT_TRUE(*handleMicroJobQueue != nullptr);
77
78    handleMicroJobQueue->SetPromiseJobQueue(thread, handlePromiseQueue.GetTaggedValue());
79    handleMicroJobQueue->SetScriptJobQueue(thread, handleScriptQueue.GetTaggedValue());
80
81    JSHandle<TaggedQueue> promiseQueue(thread, handleMicroJobQueue->GetPromiseJobQueue());
82    JSHandle<TaggedQueue> scriptQueue(thread, handleMicroJobQueue->GetScriptJobQueue());
83
84    EXPECT_EQ(promiseQueue->Size(), 1U);
85    EXPECT_EQ(scriptQueue->Size(), 0U);
86
87    EXPECT_EQ(promiseQueue->Back().GetInt(), 123);
88    EXPECT_TRUE(scriptQueue->Back().IsHole());
89}
90
91/**
92 * @tc.name: EnqueuePromiseJob
93 * @tc.desc: Get a JobQueue called MicroJobQueue from vm.define a function and TaggedArray object,call EnqueuePromiseJob
94 *           function to enter the "function" and TaggedArray object into the promise job queueof the MicroJobQueue,then
95 *           check whether the object out of the queue is the same as the object in the queue.
96 * @tc.type: FUNC
97 * @tc.require:
98 */
99HWTEST_F_L0(MicroJobQueueTest, EnqueuePromiseJob)
100{
101    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
102    JSHandle<GlobalEnv> globalEnv = thread->GetEcmaVM()->GetGlobalEnv();
103    JSHandle<MicroJobQueue> handleMicrojob = thread->GetCurrentEcmaContext()->GetMicroJobQueue();
104    JSHandle<TaggedQueue> originalPromiseQueue(thread, handleMicrojob->GetPromiseJobQueue());
105    JSHandle<JSTaggedValue> scriptQueue(thread, handleMicrojob->GetScriptJobQueue());
106
107    JSHandle<TaggedArray> arguments = factory->NewTaggedArray(2);
108    arguments->Set(thread, 0, JSTaggedValue::Undefined());
109    arguments->Set(thread, 1, JSTaggedValue::Undefined());
110    JSHandle<JSFunction> promiseReactionsJob(globalEnv->GetPromiseReactionJob());
111
112    QueueType type = QueueType::QUEUE_PROMISE;
113    MicroJobQueue::EnqueueJob(thread, handleMicrojob, type, promiseReactionsJob, arguments);
114
115    JSHandle<TaggedQueue> promiseQueue(thread, handleMicrojob->GetPromiseJobQueue());
116    EXPECT_EQ(JSTaggedValue::SameValue(promiseQueue.GetTaggedValue(), originalPromiseQueue.GetTaggedValue()), false);
117    EXPECT_EQ(JSTaggedValue::SameValue(handleMicrojob->GetScriptJobQueue(), scriptQueue.GetTaggedValue()), true);
118
119    JSTaggedValue result = promiseQueue->Pop(thread);
120    EXPECT_TRUE(result.IsPendingJob());
121
122    JSHandle<PendingJob> pendingJob(thread, result);
123    EXPECT_EQ(JSTaggedValue::SameValue(pendingJob->GetJob(), promiseReactionsJob.GetTaggedValue()), true);
124    EXPECT_EQ(JSTaggedValue::SameValue(pendingJob->GetArguments(), arguments.GetTaggedValue()), true);
125}
126
127/**
128 * @tc.name: EnqueuePromiseJob
129 * @tc.desc: Get a JobQueue called MicroJobQueue from vm.define a function and TaggedArray object,call EnqueuePromiseJob
130 *           function to enter the "function" and TaggedArray object into the script job queue of the MicroJobQueue,then
131 *           check whether the object out of the queue is the same as the object in the queue.
132 * @tc.type: FUNC
133 * @tc.require:
134 */
135HWTEST_F_L0(MicroJobQueueTest, EnqueueScriptJob)
136{
137    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
138    JSHandle<GlobalEnv> globalEnv = thread->GetEcmaVM()->GetGlobalEnv();
139    JSHandle<MicroJobQueue> handleMicrojob = thread->GetCurrentEcmaContext()->GetMicroJobQueue();
140    JSHandle<JSTaggedValue> promiseQueue(thread, handleMicrojob->GetPromiseJobQueue());
141    JSHandle<TaggedQueue> originalScriptQueue(thread, handleMicrojob->GetScriptJobQueue());
142
143    JSHandle<TaggedArray> arguments1 = factory->NewTaggedArray(2);
144    JSHandle<JSFunction> promiseReactionsJob(globalEnv->GetPromiseReactionJob());
145
146    QueueType type = QueueType::QUEUE_SCRIPT;
147    MicroJobQueue::EnqueueJob(thread, handleMicrojob, type, promiseReactionsJob, arguments1);
148
149    JSHandle<JSFunction> promiseResolveThenableJob(globalEnv->GetPromiseResolveThenableJob());
150    JSHandle<TaggedArray> arguments2 = factory->NewTaggedArray(2);
151    arguments2->Set(thread, 0, JSTaggedValue(134));
152    arguments2->Set(thread, 1, JSTaggedValue::Undefined());
153    MicroJobQueue::EnqueueJob(thread, handleMicrojob, type, promiseResolveThenableJob, arguments2);
154
155    JSHandle<TaggedQueue> scriptQueue(thread, handleMicrojob->GetScriptJobQueue());
156    EXPECT_EQ(JSTaggedValue::SameValue(scriptQueue.GetTaggedValue(), originalScriptQueue.GetTaggedValue()), false);
157    EXPECT_EQ(JSTaggedValue::SameValue(handleMicrojob->GetPromiseJobQueue(), promiseQueue.GetTaggedValue()), true);
158
159    JSTaggedValue result1 = scriptQueue->Pop(thread);
160    EXPECT_TRUE(result1.IsPendingJob());
161    // FIFO
162    JSHandle<PendingJob> pendingJob1(thread, result1);
163    EXPECT_EQ(JSTaggedValue::SameValue(pendingJob1->GetJob(), promiseReactionsJob.GetTaggedValue()), true);
164    EXPECT_EQ(JSTaggedValue::SameValue(pendingJob1->GetArguments(), arguments1.GetTaggedValue()), true);
165
166    JSTaggedValue result2 = scriptQueue->Pop(thread);
167    EXPECT_TRUE(result2.IsPendingJob());
168    JSHandle<PendingJob> pendingJob2(thread, result2);
169    EXPECT_EQ(JSTaggedValue::SameValue(pendingJob2->GetJob(), promiseResolveThenableJob.GetTaggedValue()), true);
170    EXPECT_EQ(JSTaggedValue::SameValue(pendingJob2->GetArguments(), arguments2.GetTaggedValue()), true);
171}
172
173/**
174 * @tc.name: ExecutePendingJob_001
175 * @tc.desc: Get a JobQueue called MicroJobQueue from vm and get a function called PromiseReactionJob from env.
176 *           According to the definition of function,define a TaggedArray object with length of two.set the required
177 *           value and enter "function" and TaggedArray object into the promise job queue.Calling "ExecutePendingJob"
178 *           function to execute the method of function and return the value of the method.
179 * @tc.type: FUNC
180 * @tc.require:
181 */
182HWTEST_F_L0(MicroJobQueueTest, ExecutePendingJob_001)
183{
184    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
185    auto env = thread->GetEcmaVM()->GetGlobalEnv();
186    JSHandle<JSFunction> promiseReactionsJob(env->GetPromiseReactionJob());
187    JSHandle<MicroJobQueue> handleMicrojob = thread->GetCurrentEcmaContext()->GetMicroJobQueue();
188
189    JSHandle<JSTaggedValue> promiseFunc = env->GetPromiseFunction();
190    JSHandle<PromiseCapability> capbility = JSPromise::NewPromiseCapability(thread, promiseFunc);
191    JSHandle<JSTaggedValue> resolve(thread, capbility->GetResolve());
192
193    JSHandle<PromiseReaction> fulfillReaction = factory->NewPromiseReaction();
194    fulfillReaction->SetPromiseCapability(thread, capbility.GetTaggedValue());
195    fulfillReaction->SetHandler(thread, resolve.GetTaggedValue());
196
197    JSHandle<TaggedArray> arguments = factory->NewTaggedArray(2);
198    arguments->Set(thread, 0, fulfillReaction.GetTaggedValue());
199    arguments->Set(thread, 1, JSTaggedValue::Undefined());
200    MicroJobQueue::EnqueueJob(thread, handleMicrojob, QueueType::QUEUE_PROMISE, promiseReactionsJob, arguments);
201
202    JSHandle<PromiseReaction> rejectReaction = factory->NewPromiseReaction();
203    rejectReaction->SetPromiseCapability(thread, capbility.GetTaggedValue());
204    rejectReaction->SetHandler(thread, resolve.GetTaggedValue());
205
206    // get into the promise queue and execute PendingJob
207    if (!thread->HasPendingException()) {
208        MicroJobQueue::ExecutePendingJob(thread, handleMicrojob);
209    }
210    JSHandle<JSPromise> jsPromise(thread, capbility->GetPromise());
211    EXPECT_EQ(jsPromise->GetPromiseState(), PromiseState::FULFILLED);
212    EXPECT_EQ(JSTaggedValue::SameValue(jsPromise->GetPromiseResult(), JSTaggedValue::Undefined()), true);
213}
214
215/**
216 * @tc.name: ExecutePendingJob_002
217 * @tc.desc: Get a JobQueue called MicroJobQueue from vm and get a function called PromiseReactionJob from env.
218 *           According to the definition of function,define a TaggedArray object with length of two.set the required
219 *           value and enter the "function" and TaggedArray object into the script job queue and promise job queue.
220 *           Calling "ExecutePendingJob" function to execute the method of Two queue function and return the value
221 *           of the method.
222 * @tc.type: FUNC
223 * @tc.require:
224 */
225HWTEST_F_L0(MicroJobQueueTest, ExecutePendingJob_002)
226{
227    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
228    auto env = thread->GetEcmaVM()->GetGlobalEnv();
229    JSHandle<JSFunction> promiseReactionsJob(env->GetPromiseReactionJob());
230    JSHandle<MicroJobQueue> handleMicrojob = thread->GetCurrentEcmaContext()->GetMicroJobQueue();
231
232    JSHandle<JSTaggedValue> promiseFunc = env->GetPromiseFunction();
233    JSHandle<PromiseCapability> capbility1 = JSPromise::NewPromiseCapability(thread, promiseFunc);
234    JSHandle<JSTaggedValue> resolve(thread, capbility1->GetResolve());
235
236    JSHandle<PromiseReaction> fulfillReaction = factory->NewPromiseReaction();
237    fulfillReaction->SetPromiseCapability(thread, capbility1.GetTaggedValue());
238    fulfillReaction->SetHandler(thread, resolve.GetTaggedValue());
239
240    JSHandle<TaggedArray> arguments1 = factory->NewTaggedArray(2);
241    arguments1->Set(thread, 0, fulfillReaction.GetTaggedValue());
242    arguments1->Set(thread, 1, JSTaggedValue::Undefined());
243    MicroJobQueue::EnqueueJob(thread, handleMicrojob, QueueType::QUEUE_PROMISE, promiseReactionsJob, arguments1);
244
245    JSHandle<PromiseCapability> capbility2 = JSPromise::NewPromiseCapability(thread, promiseFunc);
246    JSHandle<JSTaggedValue> reject(thread, capbility2->GetReject());
247    JSHandle<PromiseReaction> rejectReaction = factory->NewPromiseReaction();
248    rejectReaction->SetPromiseCapability(thread, capbility2.GetTaggedValue());
249    rejectReaction->SetHandler(thread, reject.GetTaggedValue());
250
251    JSHandle<TaggedArray> arguments2 = factory->NewTaggedArray(2);
252    arguments2->Set(thread, 0, rejectReaction.GetTaggedValue());
253    arguments2->Set(thread, 1, JSTaggedValue(32));
254    MicroJobQueue::EnqueueJob(thread, handleMicrojob, QueueType::QUEUE_SCRIPT, promiseReactionsJob, arguments2);
255
256    // get into the promise queue and execute PendingJob
257    if (!thread->HasPendingException()) {
258        MicroJobQueue::ExecutePendingJob(thread, handleMicrojob);
259    }
260    JSHandle<JSPromise> resolvePromise(thread, capbility1->GetPromise());
261    EXPECT_EQ(resolvePromise->GetPromiseState(), PromiseState::FULFILLED);
262    EXPECT_EQ(JSTaggedValue::SameValue(resolvePromise->GetPromiseResult(), JSTaggedValue::Undefined()), true);
263
264    JSHandle<JSPromise> rejectPromise(thread, capbility2->GetPromise());
265    EXPECT_EQ(rejectPromise->GetPromiseState(), PromiseState::REJECTED);
266    EXPECT_EQ(JSTaggedValue::SameValue(rejectPromise->GetPromiseResult(), JSTaggedValue(32)), true);
267}
268} // namespace panda::test
269