1 /*
2 * Copyright (c) 2022-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/js_map_iterator.h"
17 #include "ecmascript/global_env.h"
18 #include "ecmascript/linked_hash_table.h"
19 #include "ecmascript/js_array.h"
20 #include "ecmascript/js_map.h"
21 #include "ecmascript/tests/test_helper.h"
22
23 using namespace panda;
24 using namespace panda::ecmascript;
25
26 namespace panda::test {
27 class JSMapIteratorTest : public BaseTestWithScope<false> {
28 };
29
CreateJSMap(JSThread * thread)30 JSHandle<JSMap> CreateJSMap(JSThread *thread)
31 {
32 JSHandle<GlobalEnv> globaEnv = thread->GetEcmaVM()->GetGlobalEnv();
33 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
34
35 JSHandle<JSTaggedValue> builtinsMapFunc = globaEnv->GetBuiltinsMapFunction();
36 JSHandle<JSMap> jsMap(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(builtinsMapFunc), builtinsMapFunc));
37 JSHandle<JSTaggedValue> linkedHashMap(LinkedHashMap::Create(thread));
38 jsMap->SetLinkedMap(thread, linkedHashMap);
39 return jsMap;
40 }
41
42 /**
43 * @tc.name: CreateSetIterator
44 * @tc.desc: Call "CreateSetIterator" function create SetIterator,Check whether the the SetIterator is JSMapIterator
45 * through "IsJSMapIterator" function is within expectations.
46 * @tc.type: FUNC
47 * @tc.require:
48 */
HWTEST_F_L0(JSMapIteratorTest,CreateMapIterator)49 HWTEST_F_L0(JSMapIteratorTest, CreateMapIterator)
50 {
51 JSHandle<JSMap> jsMap = CreateJSMap(thread);
52
53 IterationKind iterKind = IterationKind::KEY;
54 JSHandle<JSTaggedValue> mapIteratorKey =
55 JSMapIterator::CreateMapIterator(thread, JSHandle<JSTaggedValue>::Cast(jsMap), iterKind);
56 EXPECT_TRUE(mapIteratorKey->IsJSMapIterator());
57
58 iterKind = IterationKind::VALUE;
59 JSHandle<JSTaggedValue> mapIteratorValue =
60 JSMapIterator::CreateMapIterator(thread, JSHandle<JSTaggedValue>::Cast(jsMap), iterKind);
61 EXPECT_TRUE(mapIteratorValue->IsJSMapIterator());
62
63 iterKind = IterationKind::KEY_AND_VALUE;
64 JSHandle<JSTaggedValue> mapIteratorKeyAndValue =
65 JSMapIterator::CreateMapIterator(thread, JSHandle<JSTaggedValue>::Cast(jsMap), iterKind);
66 EXPECT_TRUE(mapIteratorKeyAndValue->IsJSMapIterator());
67 }
68
69 /**
70 * @tc.name: SetIteratedMap
71 * @tc.desc: Create a JSMapIterator through calling "CreateMapIterator" function with the current thread. Check whether
72 * the returned value through calling "GetIteratedMap" function from the JSMapIterator is the same with the
73 * returned value through calling "GetLinkedMap" function. Call "SetIteratedMap" function change LinkedMap,
74 * Check whether the returned JSTaggedValue through calling "GetNextIndex" function from the JSMapIterator is
75 * the same with the changed value.
76 * source JSMap.
77 * @tc.type: FUNC
78 * @tc.require:
79 */
HWTEST_F_L0(JSMapIteratorTest,SetIteratedMap)80 HWTEST_F_L0(JSMapIteratorTest, SetIteratedMap)
81 {
82 IterationKind iterKind = IterationKind::KEY;
83 JSHandle<JSMap> jsMap1 = CreateJSMap(thread);
84 JSHandle<JSMap> jsMap2 = CreateJSMap(thread);
85 JSHandle<JSTaggedValue> mapIteratorVal =
86 JSMapIterator::CreateMapIterator(thread, JSHandle<JSTaggedValue>::Cast(jsMap1), iterKind);
87 JSHandle<JSMapIterator> jsMapIterator = JSHandle<JSMapIterator>::Cast(mapIteratorVal);
88
89 EXPECT_EQ(jsMap1->GetLinkedMap(), jsMapIterator->GetIteratedMap());
90 jsMapIterator->SetIteratedMap(thread, JSHandle<JSTaggedValue>(thread, jsMap2->GetLinkedMap()));
91 EXPECT_NE(jsMap1->GetLinkedMap(), jsMapIterator->GetIteratedMap());
92 EXPECT_EQ(jsMap2->GetLinkedMap(), jsMapIterator->GetIteratedMap());
93 }
94
95 /**
96 * @tc.name: SetNextIndex
97 * @tc.desc: Create a JSMapIterator through calling "CreateMapIterator" function with the current thread, Check
98 * whether the returned value through calling "GetNextIndex" function from the JSMapIterator is the same
99 * as JSTaggedValue(0) Call "SetNextIndex" to change NextIndex, Check whether the returned value through
100 * calling "GetNextIndex" function from the JSMapIterator is the same with the changed value.
101 * @tc.type: FUNC
102 * @tc.require:
103 */
HWTEST_F_L0(JSMapIteratorTest,SetNextIndex)104 HWTEST_F_L0(JSMapIteratorTest, SetNextIndex)
105 {
106 JSHandle<JSMap> jsMap = CreateJSMap(thread);
107 JSHandle<JSTaggedValue> mapValue(jsMap);
108 JSHandle<JSTaggedValue> mapIteratorVal =
109 JSMapIterator::CreateMapIterator(thread, mapValue, IterationKind::KEY);
110 JSHandle<JSMapIterator> mapIterator = JSHandle<JSMapIterator>::Cast(mapIteratorVal);
111
112 EXPECT_EQ(mapIterator->GetNextIndex(), 0U);
113 mapIterator->SetNextIndex(1);
114 EXPECT_EQ(mapIterator->GetNextIndex(), 1U);
115 }
116
117 /**
118 * @tc.name: SetIterationKind
119 * @tc.desc: Create a JSMapIterator through calling CreateMapIterator function with the current thread. Check whether
120 * the returned value through calling "GetIterationKind" function is the same with JSTaggedValue(0). Check
121 * whether the returned value through calling "GetIterationKind" function is within expectations after calling
122 * "SetIterationKind" with the current thread.
123 * @tc.type: FUNC
124 * @tc.require:
125 */
HWTEST_F_L0(JSMapIteratorTest,SetIterationKind)126 HWTEST_F_L0(JSMapIteratorTest, SetIterationKind)
127 {
128 JSHandle<JSMap> jsMap = CreateJSMap(thread);
129 JSHandle<JSTaggedValue> mapValue(jsMap);
130 JSHandle<JSTaggedValue> mapIteratorVal =
131 JSMapIterator::CreateMapIterator(thread, mapValue, IterationKind::KEY);
132 JSHandle<JSMapIterator> mapIterator = JSHandle<JSMapIterator>::Cast(mapIteratorVal);
133 EXPECT_EQ(mapIterator->GetIterationKind(), IterationKind::KEY);
134 mapIterator->SetIterationKind(IterationKind::VALUE);
135 EXPECT_EQ(mapIterator->GetIterationKind(), IterationKind::VALUE);
136 mapIterator->SetIterationKind(IterationKind::KEY_AND_VALUE);
137 EXPECT_EQ(mapIterator->GetIterationKind(), IterationKind::KEY_AND_VALUE);
138 }
139
140 /**
141 * @tc.name: Update
142 * @tc.desc: Call "NewJSMapIterator" function create MapIterator with emty IteratedMap, create other JSMap and add key
143 * to it,the old JSMap call "Rehash" function set new JSMap to the next table, then SetIterator call "Update"
144 * function upadate IteratedMap, check whether the IteratedMap is within expectations.
145 * @tc.type: FUNC
146 * @tc.require:
147 */
HWTEST_F_L0(JSMapIteratorTest,Update)148 HWTEST_F_L0(JSMapIteratorTest, Update)
149 {
150 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
151 JSHandle<JSMap> jsMap1(thread, CreateJSMap(thread).GetTaggedValue());
152 JSHandle<JSMap> jsMap2(thread, CreateJSMap(thread).GetTaggedValue());
153
154 JSHandle<JSTaggedValue> keyHandle1(thread, JSTaggedValue(0));
155 JSHandle<JSTaggedValue> keyHandle2(thread, JSTaggedValue(1));
156 JSHandle<JSTaggedValue> keyHandle3(thread, JSTaggedValue(2)); // 2: means key and value
157 // add key and value to jsMap1
158 JSMap::Set(thread, jsMap1, keyHandle1, keyHandle1);
159 JSMap::Set(thread, jsMap1, keyHandle2, keyHandle2);
160 JSMap::Set(thread, jsMap1, keyHandle3, keyHandle3);
161
162 JSHandle<LinkedHashMap> mapHandle1(thread, LinkedHashMap::Cast(jsMap1->GetLinkedMap().GetTaggedObject()));
163 JSHandle<LinkedHashMap> mapHandle2(thread, LinkedHashMap::Cast(jsMap2->GetLinkedMap().GetTaggedObject()));
164 mapHandle1->Rehash(thread, *mapHandle2);
165 // create SetIterator with jsMap1
166 JSHandle<JSMapIterator> mapIterator = factory->NewJSMapIterator(jsMap1, IterationKind::KEY);
167 // update MapIterator
168 mapIterator->Update(thread);
169 LinkedHashMap *resultMap = LinkedHashMap::Cast(mapIterator->GetIteratedMap().GetTaggedObject());
170 EXPECT_TRUE(resultMap->Has(thread, keyHandle1.GetTaggedValue()));
171 EXPECT_TRUE(resultMap->Has(thread, keyHandle2.GetTaggedValue()));
172 EXPECT_TRUE(resultMap->Has(thread, keyHandle3.GetTaggedValue()));
173 }
174
175 /**
176 * @tc.name: Next
177 * @tc.desc: Calling "Next" function get the next value from the MapIterator,Check whether the return value obtained
178 * by the function is the next value in the array element.
179 * @tc.type: FUNC
180 * @tc.require:
181 */
HWTEST_F_L0(JSMapIteratorTest,KEY_VALUE_Next)182 HWTEST_F_L0(JSMapIteratorTest, KEY_VALUE_Next)
183 {
184 JSHandle<JSMap> jsMap = CreateJSMap(thread);
185 int32_t iteratorLength = 5;
186 for (int32_t i = 0; i < iteratorLength; i++) {
187 JSMap::Set(thread, jsMap, JSHandle<JSTaggedValue>(thread, JSTaggedValue(i)),
188 JSHandle<JSTaggedValue>(thread, JSTaggedValue(i + 5))); // 5: means expected value
189 }
190 JSHandle<JSTaggedValue> mapValue(jsMap);
191 JSHandle<JSMapIterator> mapIterator(JSMapIterator::CreateMapIterator(thread, mapValue, IterationKind::KEY));
192 // 4: argv length
193 auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
194 ecmaRuntimeCallInfo1->SetThis(mapIterator.GetTaggedValue());
195 ecmaRuntimeCallInfo1->SetFunction(JSTaggedValue::Undefined());
196 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
197 for (int32_t i = 0; i < 5; i++) {
198 JSHandle<JSTaggedValue> nextTagValResult(thread, JSMapIterator::Next(ecmaRuntimeCallInfo1));
199 EXPECT_EQ(JSIterator::IteratorValue(thread, nextTagValResult)->GetInt(), i);
200 }
201 TestHelper::TearDownFrame(thread, prev);
202 // change Iterationkind to VALUE
203 mapIterator->SetIterationKind(IterationKind::VALUE);
204 mapIterator->SetNextIndex(0);
205 // 4: argv length
206 auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
207 ecmaRuntimeCallInfo2->SetThis(mapIterator.GetTaggedValue());
208 ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
209 prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2);
210 for (int32_t i = 0; i < iteratorLength; i++) {
211 JSHandle<JSTaggedValue> nextTagValResult(thread, JSMapIterator::Next(ecmaRuntimeCallInfo2));
212 EXPECT_EQ(JSIterator::IteratorValue(thread, nextTagValResult)->GetInt(), i + 5); // 5: means expected value
213 }
214 TestHelper::TearDownFrame(thread, prev);
215 }
216
HWTEST_F_L0(JSMapIteratorTest,KEY_AND_VALUE_Next)217 HWTEST_F_L0(JSMapIteratorTest, KEY_AND_VALUE_Next)
218 {
219 JSHandle<JSMap> jsMap = CreateJSMap(thread);
220 int32_t iteratorLength = 5;
221 for (int32_t i = 0; i < iteratorLength; i++) {
222 JSMap::Set(thread, jsMap, JSHandle<JSTaggedValue>(thread, JSTaggedValue(i)),
223 JSHandle<JSTaggedValue>(thread, JSTaggedValue(i + 10))); // 10: means expected value
224 }
225 JSHandle<JSTaggedValue> mapValue(jsMap);
226 JSHandle<JSMapIterator> mapIterator(
227 JSMapIterator::CreateMapIterator(thread, mapValue, IterationKind::KEY_AND_VALUE));
228 // 4: argv length
229 auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
230 ecmaRuntimeCallInfo->SetThis(mapIterator.GetTaggedValue());
231 ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
232 [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
233 for (int32_t i = 0; i < iteratorLength; i++) {
234 JSHandle<JSTaggedValue> nextTagValResult(thread, JSMapIterator::Next(ecmaRuntimeCallInfo));
235 JSHandle<JSArray> iteratorVal(thread, JSIterator::IteratorValue(thread, nextTagValResult).GetTaggedValue());
236 JSHandle<TaggedArray> elementsIterVal(thread, iteratorVal->GetElements());
237 EXPECT_EQ(i, elementsIterVal->Get(thread, 0).GetInt());
238 EXPECT_EQ(i + 10, elementsIterVal->Get(thread, 1).GetInt()); // 10: means expected value
239 }
240 TestHelper::TearDownFrame(thread, prev);
241 }
242 } // namespace panda::test
243