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 
25 using namespace panda::ecmascript;
26 
27 namespace panda::test {
28 using MicroJobQueue = ecmascript::job::MicroJobQueue;
29 using PendingJob = ecmascript::job::PendingJob;
30 using QueueType = job::QueueType;
31 class MicroJobQueueTest : public testing::Test {
32 public:
SetUpTestCase()33     static void SetUpTestCase()
34     {
35         GTEST_LOG_(INFO) << "SetUpTestCase";
36     }
37 
TearDownTestCase()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  */
HWTEST_F_L0(MicroJobQueueTest, GetJobQueue)65 HWTEST_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  */
HWTEST_F_L0(MicroJobQueueTest, EnqueuePromiseJob)99 HWTEST_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  */
HWTEST_F_L0(MicroJobQueueTest, EnqueueScriptJob)135 HWTEST_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  */
HWTEST_F_L0(MicroJobQueueTest, ExecutePendingJob_001)182 HWTEST_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  */
HWTEST_F_L0(MicroJobQueueTest, ExecutePendingJob_002)225 HWTEST_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