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_set_iterator.h"
17#include "ecmascript/global_env.h"
18#include "ecmascript/js_array.h"
19#include "ecmascript/js_set.h"
20#include "ecmascript/linked_hash_table.h"
21#include "ecmascript/tests/ecma_test_common.h"
22
23using namespace panda;
24using namespace panda::ecmascript;
25
26namespace panda::test {
27class JSSetIteratorTest : public BaseTestWithScope<false> {
28};
29
30static JSSet *CreateJSSet(JSThread *thread)
31{
32    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
33    JSHandle<JSTaggedValue> constructor = thread->GetEcmaVM()->GetGlobalEnv()->GetBuiltinsSetFunction();
34    JSHandle<JSSet> set =
35        JSHandle<JSSet>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(constructor), constructor));
36    JSHandle<LinkedHashSet> hashSet = LinkedHashSet::Create(thread);
37    set->SetLinkedSet(thread, hashSet);
38    return JSSet::Cast(set.GetTaggedValue().GetTaggedObject());
39}
40
41/**
42 * @tc.name: CreateSetIterator
43 * @tc.desc: Call "CreateSetIterator" function create SetIterator,Check whether the the attribute setting of SetIterator
44 *           through "GetNextIndex" and "GetIterationKind" function is within expectations.
45 * @tc.type: FUNC
46 * @tc.require:
47 */
48HWTEST_F_L0(JSSetIteratorTest, CreateSetIterator)
49{
50    JSHandle<JSSet> jsSet(thread, CreateJSSet(thread));
51    EXPECT_TRUE(*jsSet != nullptr);
52
53    JSHandle<JSTaggedValue> setIteratorValue1 =
54        JSSetIterator::CreateSetIterator(thread, JSHandle<JSTaggedValue>(jsSet), IterationKind::KEY);
55
56    EXPECT_EQ(setIteratorValue1->IsJSSetIterator(), true);
57    JSHandle<JSSetIterator> setIterator1(setIteratorValue1);
58    EXPECT_EQ(JSTaggedValue::SameValue(setIterator1->GetIteratedSet(), jsSet->GetLinkedSet()), true);
59    EXPECT_EQ(setIterator1->GetNextIndex(), 0U);
60    EXPECT_EQ(setIterator1->GetIterationKind(), IterationKind::KEY);
61
62    JSHandle<JSTaggedValue> setIteratorValue2 =
63        JSSetIterator::CreateSetIterator(thread, JSHandle<JSTaggedValue>(jsSet), IterationKind::VALUE);
64
65    EXPECT_EQ(setIteratorValue2->IsJSSetIterator(), true);
66    JSHandle<JSSetIterator> setIterator2(setIteratorValue2);
67    EXPECT_EQ(JSTaggedValue::SameValue(setIterator2->GetIteratedSet(), jsSet->GetLinkedSet()), true);
68    EXPECT_EQ(setIterator2->GetNextIndex(), 0U);
69    EXPECT_EQ(setIterator2->GetIterationKind(), IterationKind::VALUE);
70}
71
72/**
73 * @tc.name: Update
74 * @tc.desc: Call "NewJSSetIterator" function create SetIterator with emty IteratedSet,create other JSSet and add key
75 *           to it,the old JSSet call "Rehash" function set new JSSet to the next table, then SetIterator call "Update"
76 *           function upadate IteratedSet,check whether the IteratedSet is within expectations.
77 * @tc.type: FUNC
78 * @tc.require:
79 */
80HWTEST_F_L0(JSSetIteratorTest, Update)
81{
82    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
83    JSHandle<JSSet> jsSet1(thread, CreateJSSet(thread));
84    JSHandle<JSSet> jsSet2(thread, CreateJSSet(thread));
85
86    JSHandle<JSTaggedValue> keyHandle1(factory->NewFromASCII("key1"));
87    JSHandle<JSTaggedValue> keyHandle2(factory->NewFromASCII("key2"));
88    JSHandle<JSTaggedValue> keyHandle3(factory->NewFromASCII("key3"));
89    // add key to jsSet2
90    JSSet::Add(thread, jsSet2, keyHandle1);
91    JSSet::Add(thread, jsSet2, keyHandle2);
92    JSSet::Add(thread, jsSet2, keyHandle3);
93
94    JSHandle<LinkedHashSet> setHandle1(thread, LinkedHashSet::Cast(jsSet1->GetLinkedSet().GetTaggedObject()));
95    JSHandle<LinkedHashSet> setHandle2(thread, LinkedHashSet::Cast(jsSet2->GetLinkedSet().GetTaggedObject()));
96    setHandle1->Rehash(thread, *setHandle2);
97    // create SetIterator with jsSet1
98    JSHandle<JSSetIterator> setIterator = factory->NewJSSetIterator(jsSet1, IterationKind::KEY);
99    // update SetIterator
100    setIterator->Update(thread);
101    LinkedHashSet *resultSet = LinkedHashSet::Cast(setIterator->GetIteratedSet().GetTaggedObject());
102    EXPECT_TRUE(resultSet->Has(thread, keyHandle1.GetTaggedValue()));
103    EXPECT_TRUE(resultSet->Has(thread, keyHandle2.GetTaggedValue()));
104    EXPECT_TRUE(resultSet->Has(thread, keyHandle3.GetTaggedValue()));
105}
106
107EcmaRuntimeCallInfo* NextCommon(JSThread *thread, JSHandle<JSSetIterator>& setIterator,
108    IterationKind kind = IterationKind::KEY)
109{
110    JSHandle<JSSet> jsSet(thread, CreateJSSet(thread));
111    EXPECT_TRUE(*jsSet != nullptr);
112
113    for (int i = 0; i < 3; i++) {  // 3 : 3 default numberOfElements
114        JSHandle<JSTaggedValue> key(thread, JSTaggedValue(i));
115        JSSet::Add(thread, jsSet, key);
116    }
117    // set IterationKind(key or value)
118    JSHandle<JSTaggedValue> setIteratorValue =
119        JSSetIterator::CreateSetIterator(thread, JSHandle<JSTaggedValue>(jsSet), kind);
120    setIterator = JSHandle<JSSetIterator>(setIteratorValue);
121    std::vector<JSTaggedValue> args{JSTaggedValue::Undefined()};
122    auto ecmaRuntimeCallInfo =
123        TestHelper::CreateEcmaRuntimeCallInfo(thread, args, 6, setIteratorValue.GetTaggedValue());
124    return ecmaRuntimeCallInfo;
125}
126/**
127 * @tc.name: Next
128 * @tc.desc: get the next value in setiterator,Check whether the return value obtained by the function is
129 *           the next value in the array element.
130 * @tc.type: FUNC
131 * @tc.require:
132 */
133HWTEST_F_L0(JSSetIteratorTest, KEY_Next)
134{
135    JSHandle<JSSetIterator> setIterator;
136    auto ecmaRuntimeCallInfo = NextCommon(thread, setIterator);
137    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
138
139    for (int i = 0; i <= 3; i++) { // 3 : 3 default numberOfElements
140        JSTaggedValue result = JSSetIterator::Next(ecmaRuntimeCallInfo);
141        JSHandle<JSTaggedValue> resultObj(thread, result);
142        if (i < 3) {
143            EXPECT_EQ(setIterator->GetNextIndex(), static_cast<uint32_t>(i + 1));
144            EXPECT_EQ(i, JSIterator::IteratorValue(thread, resultObj)->GetInt());
145        }
146        else {
147            EXPECT_EQ(JSIterator::IteratorValue(thread, resultObj).GetTaggedValue(), JSTaggedValue::Undefined());
148        }
149    }
150    TestHelper::TearDownFrame(thread, prev);
151}
152
153HWTEST_F_L0(JSSetIteratorTest, KEY_AND_VALUE_Next)
154{
155    JSHandle<JSTaggedValue> index0(thread, JSTaggedValue(0));
156    JSHandle<JSTaggedValue> index1(thread, JSTaggedValue(1));
157    JSHandle<JSSetIterator> setIterator;
158    auto ecmaRuntimeCallInfo = NextCommon(thread, setIterator, IterationKind::KEY_AND_VALUE);
159    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
160
161    for (int i = 0; i <= 3; i++) { // 3 : 3 default numberOfElements
162        JSTaggedValue result = JSSetIterator::Next(ecmaRuntimeCallInfo);
163        JSHandle<JSTaggedValue> resultObj(thread, result);
164        if (i < 3) {
165            JSHandle<JSArray> arrayList(thread, JSIterator::IteratorValue(thread, resultObj).GetTaggedValue());
166            EXPECT_EQ(setIterator->GetNextIndex(), static_cast<uint32_t>(i+1));
167            EXPECT_EQ(JSArray::GetProperty(thread, JSHandle<JSTaggedValue>(arrayList), index0).GetValue()->GetInt(), i);
168            EXPECT_EQ(JSArray::GetProperty(thread, JSHandle<JSTaggedValue>(arrayList), index1).GetValue()->GetInt(), i);
169        }
170        else {
171            EXPECT_EQ(JSIterator::IteratorValue(thread, resultObj).GetTaggedValue(), JSTaggedValue::Undefined());
172        }
173    }
174    TestHelper::TearDownFrame(thread, prev);
175}
176}  // namespace panda::test
177