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/js_api/js_api_lightweightset.h"
17#include "ecmascript/containers/containers_private.h"
18#include "ecmascript/ecma_string.h"
19#include "ecmascript/ecma_vm.h"
20#include "ecmascript/global_env.h"
21#include "ecmascript/js_api/js_api_lightweightset_iterator.h"
22#include "ecmascript/js_function.h"
23#include "ecmascript/js_handle.h"
24#include "ecmascript/js_iterator.h"
25#include "ecmascript/js_object-inl.h"
26#include "ecmascript/js_tagged_value.h"
27#include "ecmascript/object_factory.h"
28#include "ecmascript/tests/ecma_test_common.h"
29
30using namespace panda;
31using namespace panda::ecmascript;
32
33namespace panda::test {
34class JSAPILightWeightSetTest : public BaseTestWithScope<false> {
35protected:
36    JSAPILightWeightSet *CreateLightWeightSet()
37    {
38        return EcmaContainerCommon::CreateLightWeightSet(thread);
39    }
40};
41
42HWTEST_F_L0(JSAPILightWeightSetTest, LightWeightSetCreate)
43{
44    JSAPILightWeightSet *lightweightSet = CreateLightWeightSet();
45    EXPECT_TRUE(lightweightSet != nullptr);
46}
47
48HWTEST_F_L0(JSAPILightWeightSetTest, AddIncreaseCapacityAddAll)
49{
50    constexpr uint32_t NODE_NUMBERS = 8; // 8 means the value
51    JSHandle<JSAPILightWeightSet> lws(thread, CreateLightWeightSet());
52    JSHandle<JSAPILightWeightSet> srcLws(thread, CreateLightWeightSet());
53    JSHandle<JSAPILightWeightSet> destLws(thread, CreateLightWeightSet());
54    JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
55
56    // test IncreaseCapacityTo
57    std::string myValue("myvalue");
58    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
59        bool result = JSAPILightWeightSet::Add(thread, lws, JSHandle<JSTaggedValue>(thread, JSTaggedValue(i)));
60        EXPECT_TRUE(result);
61    }
62    EXPECT_EQ(lws->GetSize(), NODE_NUMBERS);
63
64    uint32_t tmp = NODE_NUMBERS * 2; // 2 means the value
65    JSAPILightWeightSet::IncreaseCapacityTo(thread, lws, static_cast<int32_t>(tmp));
66    uint32_t capacity = TaggedArray::Cast(lws->GetValues().GetTaggedObject())->GetLength();
67    EXPECT_EQ(JSTaggedValue(capacity), JSTaggedValue(tmp));
68
69    // test IncreaseCapacityTo exception
70    JSAPILightWeightSet::IncreaseCapacityTo(thread, lws, 0);
71    EXPECT_EXCEPTION();
72    JSAPILightWeightSet::IncreaseCapacityTo(thread, lws, NODE_NUMBERS);
73    EXPECT_EXCEPTION();
74
75    // test AddAll
76    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
77        bool result = JSAPILightWeightSet::Add(thread, destLws, JSHandle<JSTaggedValue>(thread, JSTaggedValue(i)));
78        EXPECT_TRUE(result);
79    }
80
81    for (uint32_t i = 0; i < NODE_NUMBERS + 2; i++) {
82        JSAPILightWeightSet::Add(thread, srcLws, JSHandle<JSTaggedValue>(thread, JSTaggedValue(i)));
83    }
84    bool result = JSAPILightWeightSet::AddAll(thread, destLws, JSHandle<JSTaggedValue>::Cast(srcLws));
85    EXPECT_TRUE(result);
86    tmp = NODE_NUMBERS + 2; // 2 means the value
87    EXPECT_EQ(destLws->GetSize(), tmp);
88}
89
90HWTEST_F_L0(JSAPILightWeightSetTest, EqualClearNotEqual)
91{
92    constexpr uint32_t NODE_NUMBERS = 8; // 8 means the value
93    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
94    JSHandle<JSAPILightWeightSet> lws(thread, CreateLightWeightSet());
95    JSHandle<JSAPILightWeightSet> equalLws(thread, CreateLightWeightSet());
96    JSHandle<JSTaggedValue> jsArray = JSArray::ArrayCreate(thread, JSTaggedNumber(0));
97    JSMutableHandle<JSTaggedValue> value1(thread, JSTaggedValue::Undefined());
98    JSMutableHandle<JSTaggedValue> value2(thread, JSTaggedValue::Undefined());
99    bool result = false;
100
101    // test Equal of two empty lightweightset
102    result = JSAPILightWeightSet::Equal(thread, lws, JSHandle<JSTaggedValue>::Cast(equalLws));
103    EXPECT_FALSE(result);
104    result = JSAPILightWeightSet::Equal(thread, lws, jsArray);
105    EXPECT_FALSE(result);
106
107    // test equal
108    std::string myValue1("myvalue");
109    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
110        std::string iValue = myValue1 + std::to_string(i);
111        value1.Update(factory->NewFromStdString(iValue).GetTaggedValue());
112        result = JSAPILightWeightSet::Add(thread, lws, value1);
113        EXPECT_TRUE(result);
114    }
115    EXPECT_EQ(lws->GetSize(), NODE_NUMBERS);
116
117    std::string myValue2("myvalue");
118    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
119        std::string iValue = myValue2 + std::to_string(i);
120        value2.Update(factory->NewFromStdString(iValue).GetTaggedValue());
121        result = JSAPILightWeightSet::Add(thread, equalLws, value2);
122        EXPECT_TRUE(result);
123    }
124    EXPECT_EQ(equalLws->GetSize(), NODE_NUMBERS);
125    result = JSAPILightWeightSet::Equal(thread, lws, JSHandle<JSTaggedValue>::Cast(equalLws));
126    EXPECT_FALSE(result);
127
128    equalLws->Clear(thread);
129    EXPECT_EQ(equalLws->GetSize(), static_cast<uint32_t>(0)); // 0 means the value
130
131    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
132        std::string iValue = myValue2 + std::to_string(i);
133        if (i == 2) {
134            LOG_ECMA(ERROR) << " {} " << iValue;
135        } else {
136            value2.Update(factory->NewFromStdString(iValue).GetTaggedValue());
137            result = JSAPILightWeightSet::Add(thread, equalLws, value2);
138            EXPECT_TRUE(result);
139        }
140    }
141    EXPECT_EQ(equalLws->GetSize(), NODE_NUMBERS - 1);
142    result = JSAPILightWeightSet::Equal(thread, lws, JSHandle<JSTaggedValue>::Cast(equalLws));
143    EXPECT_FALSE(result);
144}
145
146HWTEST_F_L0(JSAPILightWeightSetTest, IsEmptyHasHasAll)
147{
148    constexpr uint32_t NODE_NUMBERS = 8;
149    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
150    JSHandle<JSAPILightWeightSet> lws(thread, CreateLightWeightSet());
151    JSHandle<JSAPILightWeightSet> hasAllLws(thread, CreateLightWeightSet());
152    JSMutableHandle<JSTaggedValue> value1(thread, JSTaggedValue::Undefined());
153    JSMutableHandle<JSTaggedValue> value2(thread, JSTaggedValue::Undefined());
154    JSMutableHandle<JSTaggedValue> value3(thread, JSTaggedValue::Undefined());
155    bool result = false;
156    std::string tValue;
157    // test IsEmpty
158    result = lws->IsEmpty();
159    EXPECT_TRUE(result);
160    // test Has
161    std::string myValue1("myvalue");
162    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
163        std::string iValue = myValue1 + std::to_string(i);
164        value1.Update(factory->NewFromStdString(iValue).GetTaggedValue());
165        result = JSAPILightWeightSet::Add(thread, lws, value1);
166        EXPECT_TRUE(result);
167    }
168    EXPECT_EQ(lws->GetSize(), NODE_NUMBERS);
169
170    tValue = myValue1 + std::to_string(5);
171    value1.Update(factory->NewFromStdString(tValue).GetTaggedValue());
172    result = lws->Has(thread, value1);
173    EXPECT_TRUE(result);
174    tValue = myValue1 + std::to_string(NODE_NUMBERS);
175    value1.Update(factory->NewFromStdString(tValue).GetTaggedValue());
176    result = lws->Has(thread, value1);
177    EXPECT_FALSE(result);
178
179    std::string myValue2("myvalue");
180    for (uint32_t i = 0; i < NODE_NUMBERS - 5; i++) {
181        if (i == 1) {
182            std::string myValue3("destValue");
183            std::string iValue3 = myValue3 + std::to_string(i);
184            value3.Update(factory->NewFromStdString(iValue3).GetTaggedValue());
185            result = JSAPILightWeightSet::Add(thread, hasAllLws, value3);
186        } else {
187            std::string iValue = myValue2 + std::to_string(i);
188            value2.Update(factory->NewFromStdString(iValue).GetTaggedValue());
189            result = JSAPILightWeightSet::Add(thread, hasAllLws, value2);
190            EXPECT_TRUE(result);
191        }
192    }
193    EXPECT_EQ(hasAllLws->GetSize(), NODE_NUMBERS - 5); // 5 means the value
194    result = lws->HasAll(JSHandle<JSTaggedValue>::Cast(hasAllLws));
195    EXPECT_FALSE(result);
196    result = hasAllLws->HasAll(JSHandle<JSTaggedValue>::Cast(lws));
197    EXPECT_FALSE(result);
198}
199
200HWTEST_F_L0(JSAPILightWeightSetTest, GetIndexOfRemoveRemoveAtGetValueAt)
201{
202    constexpr uint32_t NODE_NUMBERS = 8; // 8 means the value
203    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
204    JSHandle<JSAPILightWeightSet> lws(thread, CreateLightWeightSet());
205    JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
206    bool result = false;
207
208    // test GetSize
209    std::string myValue("myvalue");
210    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
211        std::string iValue = myValue + std::to_string(i);
212        value.Update(factory->NewFromStdString(iValue).GetTaggedValue());
213        result = JSAPILightWeightSet::Add(thread, lws, value);
214    }
215    EXPECT_EQ(lws->GetSize(), NODE_NUMBERS);
216
217    // test GetIndexOf
218    std::string tValue("myvalue5");
219    value.Update(factory->NewFromStdString(tValue).GetTaggedValue());
220    int32_t index = lws->GetIndexOf(thread, value);
221    EXPECT_EQ(index, 5); // 5 means the value
222
223    // test GetValueAt
224    JSTaggedValue jsValue = lws->GetValueAt(5); // 5 means the value
225    EXPECT_EQ(value.GetTaggedValue(), jsValue);
226
227    // test Remove
228    jsValue = lws->Remove(thread, value);
229    EXPECT_EQ(value.GetTaggedValue(), jsValue);
230    jsValue = lws->Remove(thread, value);
231    EXPECT_EQ(jsValue, JSTaggedValue::Undefined());
232
233    // test RemoveAt
234    result = lws->RemoveAt(thread, 4); // 4 means the value
235    EXPECT_EQ(lws->GetSize(), NODE_NUMBERS - 2); // 2 means the value
236    EXPECT_TRUE(result);
237    result = lws->RemoveAt(thread, -1);
238    EXPECT_FALSE(result);
239    result = lws->RemoveAt(thread, static_cast<int32_t>(NODE_NUMBERS));
240    EXPECT_FALSE(result);
241}
242
243HWTEST_F_L0(JSAPILightWeightSetTest, Iterator)
244{
245    constexpr uint32_t NODE_NUMBERS = 8; // 8 means the value
246    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
247    JSHandle<JSAPILightWeightSet> lws(thread, CreateLightWeightSet());
248
249    JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
250    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
251        value.Update(JSTaggedValue(i));
252        JSAPILightWeightSet::Add(thread, lws, value);
253    }
254
255    JSHandle<JSTaggedValue> valueIter(factory->NewJSAPILightWeightSetIterator(lws, IterationKind::VALUE));
256    JSMutableHandle<JSTaggedValue> valueIterResult(thread, JSTaggedValue::Undefined());
257    for (int i = 0; i < static_cast<int>(NODE_NUMBERS); i++) {
258        valueIterResult.Update(JSIterator::IteratorStep(thread, valueIter).GetTaggedValue());
259        int v = JSIterator::IteratorValue(thread, valueIterResult)->GetInt();
260        EXPECT_TRUE(v == i);
261    }
262}
263
264HWTEST_F_L0(JSAPILightWeightSetTest, RBTreeGetHashIndex)
265{
266    std::vector<int> hashCollisionVector = {4307, 5135, 5903, 6603, 6780, 8416, 1224, 1285, 1463, 9401, 9740};
267    uint32_t NODE_NUMBERS = static_cast<uint32_t>(hashCollisionVector.size());
268    JSHandle<JSAPILightWeightSet> lws(thread, CreateLightWeightSet());
269
270    JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
271    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
272        value.Update(JSTaggedValue(hashCollisionVector[i]));
273        JSAPILightWeightSet::Add(thread, lws, value);
274    }
275    int32_t size = static_cast<uint32_t>(lws->GetLength());
276    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
277        value.Update(JSTaggedValue(hashCollisionVector[i]));
278        int32_t index = lws->GetHashIndex(thread, value, size);
279        EXPECT_TRUE(0 <= index && index < size);
280    }
281}
282
283HWTEST_F_L0(JSAPILightWeightSetTest, SpecialReturnTestEnsureCapacityGetValueAtGetHashAt)
284{
285    constexpr uint32_t NODE_NUMBERS = 8;
286    JSHandle<JSAPILightWeightSet> lws(thread, CreateLightWeightSet());
287
288    JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
289    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
290        value.Update(JSTaggedValue(i));
291        JSAPILightWeightSet::Add(thread, lws, value);
292    }
293
294    // test special return of EnsureCapacity
295    JSHandle<TaggedArray> array(thread, lws->GetValues());
296    JSAPILightWeightSet::EnsureCapacity(thread, lws, 0);
297    JSHandle<TaggedArray> newArray(thread, lws->GetValues());
298    EXPECT_TRUE(array->GetLength() == newArray->GetLength());
299
300    // test special return of GetValueAt
301    JSTaggedValue result1 = lws->GetValueAt(-1);
302    EXPECT_EQ(result1, JSTaggedValue::Undefined());
303    JSTaggedValue result2 = lws->GetValueAt(static_cast<int32_t>(NODE_NUMBERS * 2));
304    EXPECT_EQ(result2, JSTaggedValue::Undefined());
305
306    // test special return of GetHashAt
307    JSTaggedValue result3 = lws->GetHashAt(-1);
308    EXPECT_EQ(result3, JSTaggedValue::Undefined());
309    JSTaggedValue result4 = lws->GetHashAt(static_cast<int32_t>(NODE_NUMBERS * 2));
310    EXPECT_EQ(result4, JSTaggedValue::Undefined());
311}
312
313HWTEST_F_L0(JSAPILightWeightSetTest, GetHashAtHasHash)
314{
315    constexpr uint32_t NODE_NUMBERS = 8;
316    JSHandle<JSAPILightWeightSet> lws(thread, CreateLightWeightSet());
317
318    JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
319    JSMutableHandle<JSTaggedValue> hash(thread, JSTaggedValue::Undefined());
320    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
321        value.Update(JSTaggedValue(i));
322        JSAPILightWeightSet::Add(thread, lws, value);
323    }
324
325    // test GetHashAt
326    int32_t size = static_cast<int32_t>(lws->GetLength());
327    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
328        hash.Update(JSTaggedValue(lws->Hash(thread, JSTaggedValue(i))));
329        int32_t index = lws->GetHashIndex(thread, hash, size);
330        JSTaggedValue getHash= lws->GetHashAt(index);
331        EXPECT_EQ(getHash, hash.GetTaggedValue());
332    }
333
334    // test HasHash
335    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
336        hash.Update(JSTaggedValue(lws->Hash(thread, JSTaggedValue(i))));
337        EXPECT_TRUE(lws->HasHash(hash));
338    }
339    hash.Update(JSTaggedValue(lws->Hash(thread, JSTaggedValue(NODE_NUMBERS))));
340    EXPECT_FALSE(lws->HasHash(hash));
341}
342
343HWTEST_F_L0(JSAPILightWeightSetTest, ToString)
344{
345    constexpr uint32_t NODE_NUMBERS = 3;
346    JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
347
348    JSHandle<JSAPILightWeightSet> lws(thread, CreateLightWeightSet());
349    JSTaggedValue result1 = JSAPILightWeightSet::ToString(thread, lws);
350    JSHandle<EcmaString> resultHandle1(thread, result1);
351    JSHandle<EcmaString> det = thread->GetEcmaVM()->GetFactory()->NewFromASCII("");
352    ASSERT_EQ(EcmaStringAccessor::Compare(instance, resultHandle1, det), 0);
353    for (uint32_t i = 0; i < NODE_NUMBERS; i++) {
354        value.Update(JSTaggedValue(i));
355        JSAPILightWeightSet::Add(thread, lws, value);
356    }
357    JSHandle<EcmaString> str = thread->GetEcmaVM()->GetFactory()->NewFromASCII("0,1,2");
358    JSTaggedValue result = JSAPILightWeightSet::ToString(thread, lws);
359    JSHandle<EcmaString> resultHandle(thread, result);
360    ASSERT_EQ(EcmaStringAccessor::Compare(instance, resultHandle, str), 0);
361}
362}  // namespace panda::test
363