1/*
2 * Copyright (c) 2022 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/containers/containers_deque.h"
17#include "ecmascript/containers/containers_private.h"
18#include "ecmascript/ecma_runtime_call_info.h"
19#include "ecmascript/global_env.h"
20#include "ecmascript/js_api/js_api_deque.h"
21#include "ecmascript/js_api/js_api_deque_iterator.h"
22#include "ecmascript/js_handle.h"
23#include "ecmascript/js_tagged_value-inl.h"
24#include "ecmascript/js_thread.h"
25#include "ecmascript/object_factory.h"
26#include "ecmascript/tests/test_helper.h"
27#include "ecmascript/containers/tests/containers_test_helper.h"
28
29using namespace panda::ecmascript;
30using namespace panda::ecmascript::containers;
31
32namespace panda::test {
33class ContainersDequeTest : public testing::Test {
34public:
35    static void SetUpTestCase()
36    {
37        GTEST_LOG_(INFO) << "SetUpTestCase";
38    }
39
40    static void TearDownTestCase()
41    {
42        GTEST_LOG_(INFO) << "TearDownCase";
43    }
44
45    void SetUp() override
46    {
47        TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
48    }
49
50    void TearDown() override
51    {
52        TestHelper::DestroyEcmaVMWithScope(instance, scope);
53    }
54
55    EcmaVM *instance {nullptr};
56    EcmaHandleScope *scope {nullptr};
57    JSThread *thread {nullptr};
58
59    class TestClass : public base::BuiltinsBase {
60    public:
61        static JSTaggedValue TestForEachFunc(EcmaRuntimeCallInfo *argv)
62        {
63            JSHandle<JSTaggedValue> value = GetCallArg(argv, 0);
64            JSHandle<JSTaggedValue> key = GetCallArg(argv, 1);
65            JSHandle<JSTaggedValue> deque = GetCallArg(argv, 2); // 2 means the secode arg
66            if (!deque->IsUndefined()) {
67                if (value->IsNumber()) {
68                    TaggedArray *elements = TaggedArray::Cast(JSAPIDeque::Cast(deque.GetTaggedValue().
69                                            GetTaggedObject())->GetElements().GetTaggedObject());
70                    JSTaggedValue result = elements->Get(key->GetInt());
71                    EXPECT_EQ(result, value.GetTaggedValue());
72                }
73            }
74            return JSTaggedValue::True();
75        }
76    };
77protected:
78    JSTaggedValue InitializeDequeConstructor()
79    {
80        ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
81        JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
82
83        JSHandle<JSTaggedValue> globalObject = env->GetJSGlobalObject();
84        JSHandle<JSTaggedValue> key(factory->NewFromASCII("ArkPrivate"));
85        JSHandle<JSTaggedValue> value =
86            JSObject::GetProperty(thread, JSHandle<JSTaggedValue>(globalObject), key).GetValue();
87
88        auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
89        objCallInfo->SetFunction(JSTaggedValue::Undefined());
90        objCallInfo->SetThis(value.GetTaggedValue());
91        objCallInfo->SetCallArg(0, JSTaggedValue(static_cast<int>(ContainerTag::Deque)));
92        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo);
93        JSTaggedValue result = ContainersPrivate::Load(objCallInfo);
94        TestHelper::TearDownFrame(thread, prev);
95
96        return result;
97    }
98
99    JSHandle<JSAPIDeque> CreateJSAPIDeque(JSTaggedValue compare = JSTaggedValue::Undefined())
100    {
101        JSHandle<JSTaggedValue> compareHandle(thread, compare);
102        JSHandle<JSFunction> newTarget(thread, InitializeDequeConstructor());
103        auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
104        objCallInfo->SetFunction(newTarget.GetTaggedValue());
105        objCallInfo->SetNewTarget(newTarget.GetTaggedValue());
106        objCallInfo->SetThis(JSTaggedValue::Undefined());
107        objCallInfo->SetCallArg(0, compareHandle.GetTaggedValue());
108
109        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo);
110        JSTaggedValue result = ContainersDeque::DequeConstructor(objCallInfo);
111        TestHelper::TearDownFrame(thread, prev);
112        JSHandle<JSAPIDeque> deque(thread, result);
113        return deque;
114    }
115};
116
117HWTEST_F_L0(ContainersDequeTest, DequeConstructor)
118{
119    InitializeDequeConstructor();
120    JSHandle<JSFunction> newTarget(thread, InitializeDequeConstructor());
121
122    auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
123    objCallInfo->SetFunction(newTarget.GetTaggedValue());
124    objCallInfo->SetNewTarget(newTarget.GetTaggedValue());
125    objCallInfo->SetThis(JSTaggedValue::Undefined());
126
127    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, objCallInfo);
128    JSTaggedValue result = ContainersDeque::DequeConstructor(objCallInfo);
129    TestHelper::TearDownFrame(thread, prev);
130
131    ASSERT_TRUE(result.IsJSAPIDeque());
132    JSHandle<JSAPIDeque> deque(thread, result);
133    JSTaggedValue resultProto = JSTaggedValue::GetPrototype(thread, JSHandle<JSTaggedValue>(deque));
134    JSTaggedValue funcProto = newTarget->GetFunctionPrototype();
135    ASSERT_EQ(resultProto, funcProto);
136
137    // test DequeConstructor exception
138    objCallInfo->SetNewTarget(JSTaggedValue::Undefined());
139    CONTAINERS_API_EXCEPTION_TEST(ContainersDeque, DequeConstructor, objCallInfo);
140}
141
142HWTEST_F_L0(ContainersDequeTest, InsertFrontAndGetFirst)
143{
144    constexpr uint32_t NODE_NUMBERS = 8;
145    JSHandle<JSAPIDeque> deque = CreateJSAPIDeque();
146    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
147        auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
148        callInfo->SetFunction(JSTaggedValue::Undefined());
149        callInfo->SetThis(deque.GetTaggedValue());
150        callInfo->SetCallArg(0, JSTaggedValue(i));
151
152        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
153        JSTaggedValue result = ContainersDeque::InsertFront(callInfo);
154        TestHelper::TearDownFrame(thread, prev);
155        EXPECT_EQ(result, JSTaggedValue::True());
156        EXPECT_EQ(ContainersDeque::GetFirst(callInfo), JSTaggedValue(i));
157    }
158}
159
160HWTEST_F_L0(ContainersDequeTest, InsertEndAndGetLast)
161{
162    constexpr uint32_t NODE_NUMBERS = 8;
163    JSHandle<JSAPIDeque> deque = CreateJSAPIDeque();
164    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
165        auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
166        callInfo->SetFunction(JSTaggedValue::Undefined());
167        callInfo->SetThis(deque.GetTaggedValue());
168        callInfo->SetCallArg(0, JSTaggedValue(i));
169
170        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
171        JSTaggedValue result = ContainersDeque::InsertEnd(callInfo);
172        TestHelper::TearDownFrame(thread, prev);
173        EXPECT_EQ(result, JSTaggedValue::True());
174        EXPECT_EQ(ContainersDeque::GetLast(callInfo), JSTaggedValue(i));
175    }
176}
177
178HWTEST_F_L0(ContainersDequeTest, Has)
179{
180    constexpr uint32_t NODE_NUMBERS = 8;
181    JSHandle<JSAPIDeque> deque = CreateJSAPIDeque();
182    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
183        auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
184        callInfo->SetFunction(JSTaggedValue::Undefined());
185        callInfo->SetThis(deque.GetTaggedValue());
186        callInfo->SetCallArg(0, JSTaggedValue(i));
187
188        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
189        ContainersDeque::InsertEnd(callInfo);
190        TestHelper::TearDownFrame(thread, prev);
191    }
192
193    int num = 7;
194    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
195        auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
196        callInfo->SetFunction(JSTaggedValue::Undefined());
197        callInfo->SetThis(deque.GetTaggedValue());
198        callInfo->SetCallArg(0, JSTaggedValue(i));
199
200        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
201        JSTaggedValue result = ContainersDeque::Has(callInfo);
202        TestHelper::TearDownFrame(thread, prev);
203        EXPECT_EQ(result, JSTaggedValue::True());
204    }
205    num = 7;
206    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
207        auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
208        callInfo->SetFunction(JSTaggedValue::Undefined());
209        callInfo->SetThis(deque.GetTaggedValue());
210        callInfo->SetCallArg(0, JSTaggedValue(i + 8));
211
212        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
213        JSTaggedValue result = ContainersDeque::Has(callInfo);
214        TestHelper::TearDownFrame(thread, prev);
215        EXPECT_EQ(result, JSTaggedValue::False());
216    }
217}
218
219HWTEST_F_L0(ContainersDequeTest, PopFirst)
220{
221    constexpr uint32_t NODE_NUMBERS = 8;
222    JSHandle<JSAPIDeque> deque = CreateJSAPIDeque();
223    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
224        auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
225        callInfo->SetFunction(JSTaggedValue::Undefined());
226        callInfo->SetThis(deque.GetTaggedValue());
227        callInfo->SetCallArg(0, JSTaggedValue(i));
228
229        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
230        ContainersDeque::InsertFront(callInfo);
231        TestHelper::TearDownFrame(thread, prev);
232        EXPECT_EQ(ContainersDeque::PopFirst(callInfo), JSTaggedValue(i));
233    }
234}
235
236HWTEST_F_L0(ContainersDequeTest, PopLast)
237{
238    constexpr uint32_t NODE_NUMBERS = 8;
239    JSHandle<JSAPIDeque> deque = CreateJSAPIDeque();
240    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
241        auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
242        callInfo->SetFunction(JSTaggedValue::Undefined());
243        callInfo->SetThis(deque.GetTaggedValue());
244        callInfo->SetCallArg(0, JSTaggedValue(i));
245
246        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
247        ContainersDeque::InsertEnd(callInfo);
248        TestHelper::TearDownFrame(thread, prev);
249        EXPECT_EQ(ContainersDeque::PopLast(callInfo), JSTaggedValue(i));
250    }
251}
252
253HWTEST_F_L0(ContainersDequeTest, ForEach)
254{
255    constexpr uint32_t NODE_NUMBERS = 8;
256    JSHandle<JSAPIDeque> deque = CreateJSAPIDeque();
257    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
258            auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
259            callInfo->SetFunction(JSTaggedValue::Undefined());
260            callInfo->SetThis(deque.GetTaggedValue());
261            callInfo->SetCallArg(0, JSTaggedValue(i));
262
263            [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
264            ContainersDeque::InsertEnd(callInfo);
265            TestHelper::TearDownFrame(thread, prev);
266    }
267    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
268    JSHandle<JSAPIDeque> dlist = CreateJSAPIDeque();
269    {
270        JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
271        JSHandle<JSFunction> func = factory->NewJSFunction(env, reinterpret_cast<void *>(TestClass::TestForEachFunc));
272        auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
273        callInfo->SetFunction(JSTaggedValue::Undefined());
274        callInfo->SetThis(deque.GetTaggedValue());
275        callInfo->SetCallArg(0, func.GetTaggedValue());
276        callInfo->SetCallArg(1, dlist.GetTaggedValue());
277
278        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
279        ContainersDeque::ForEach(callInfo);
280        TestHelper::TearDownFrame(thread, prev);
281    }
282}
283
284HWTEST_F_L0(ContainersDequeTest, ProxyOfGetSize)
285{
286    constexpr uint32_t NODE_NUMBERS = 10;
287    JSHandle<JSAPIDeque> deque = CreateJSAPIDeque();
288    auto callInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
289    callInfo->SetFunction(JSTaggedValue::Undefined());
290    JSHandle<JSProxy> proxy = CreateJSProxyHandle(thread);
291    proxy->SetTarget(thread, deque.GetTaggedValue());
292    callInfo->SetThis(proxy.GetTaggedValue());
293
294    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
295        callInfo->SetCallArg(0, JSTaggedValue(i));
296        callInfo->SetCallArg(1, JSTaggedValue(i + 1));
297        [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, callInfo);
298        ContainersDeque::InsertFront(callInfo);
299        TestHelper::TearDownFrame(thread, prev);
300
301        [[maybe_unused]] auto prev1 = TestHelper::SetupFrame(thread, callInfo);
302        JSTaggedValue retult = ContainersDeque::GetSize(callInfo);
303        TestHelper::TearDownFrame(thread, prev1);
304        EXPECT_EQ(retult, JSTaggedValue(i + 1));
305    }
306}
307
308HWTEST_F_L0(ContainersDequeTest, ExceptionReturn)
309{
310    CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersDeque, InsertFront);
311    CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersDeque, InsertEnd);
312    CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersDeque, GetFirst);
313    CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersDeque, GetLast);
314    CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersDeque, Has);
315    CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersDeque, PopFirst);
316    CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersDeque, PopLast);
317    CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersDeque, ForEach);
318    CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersDeque, GetIteratorObj);
319    CONTAINERS_API_TYPE_MISMATCH_EXCEPTION_TEST(ContainersDeque, GetSize);
320}
321}  // namespace panda::test
322