/* * Copyright (c) 2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ecmascript/ecma_string.h" #include "ecmascript/containers/containers_private.h" #include "ecmascript/ecma_string.h" #include "ecmascript/ecma_vm.h" #include "ecmascript/global_env.h" #include "ecmascript/js_api/js_api_lightweightmap.h" #include "ecmascript/js_api/js_api_lightweightmap_iterator.h" #include "ecmascript/js_function.h" #include "ecmascript/js_handle.h" #include "ecmascript/js_iterator.h" #include "ecmascript/js_object-inl.h" #include "ecmascript/js_tagged_value.h" #include "ecmascript/object_factory.h" #include "ecmascript/tests/test_helper.h" using namespace panda; using namespace panda::ecmascript; using namespace panda::ecmascript::containers; namespace panda::test { class JSAPILightWeightMapTest : public testing::Test { public: const static int DEFAULT_SIZE = 8; static void SetUpTestCase() { GTEST_LOG_(INFO) << "SetUpTestCase"; } static void TearDownTestCase() { GTEST_LOG_(INFO) << "TearDownCase"; } void SetUp() override { TestHelper::CreateEcmaVMWithScope(instance, thread, scope); } void TearDown() override { TestHelper::DestroyEcmaVMWithScope(instance, scope); } EcmaVM *instance {nullptr}; ecmascript::EcmaHandleScope *scope {nullptr}; JSThread *thread {nullptr}; protected: JSAPILightWeightMap *CreateLightWeightMap() { JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle globalObject = env->GetJSGlobalObject(); JSHandle key(factory->NewFromASCII("ArkPrivate")); JSHandle value = JSObject::GetProperty(thread, JSHandle(globalObject), key).GetValue(); auto objCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6); objCallInfo->SetFunction(JSTaggedValue::Undefined()); objCallInfo->SetThis(value.GetTaggedValue()); objCallInfo->SetCallArg(0, JSTaggedValue(static_cast(containers::ContainerTag::LightWeightMap))); auto prev = TestHelper::SetupFrame(thread, objCallInfo); JSHandle constructor = JSHandle(thread, containers::ContainersPrivate::Load(objCallInfo)); TestHelper::TearDownFrame(thread, prev); JSHandle lightWeightMap = JSHandle:: Cast(factory->NewJSObjectByConstructor(JSHandle(constructor), constructor)); JSHandle hashArray = JSHandle(factory->NewTaggedArray(DEFAULT_SIZE)); JSHandle keyArray = JSHandle(factory->NewTaggedArray(DEFAULT_SIZE)); JSHandle valueArray = JSHandle(factory->NewTaggedArray(DEFAULT_SIZE)); lightWeightMap->SetHashes(thread, hashArray); lightWeightMap->SetKeys(thread, keyArray); lightWeightMap->SetValues(thread, valueArray); lightWeightMap->SetLength(0); return *lightWeightMap; } }; HWTEST_F_L0(JSAPILightWeightMapTest, LightWeightMapCreate) { JSAPILightWeightMap *lightWeightMap = CreateLightWeightMap(); EXPECT_TRUE(lightWeightMap != nullptr); } HWTEST_F_L0(JSAPILightWeightMapTest, SetHasKeyGetHasValue) { JSAPILightWeightMap *lightWeightMap = CreateLightWeightMap(); JSHandle key(thread, JSTaggedValue(1)); JSHandle value(thread, JSTaggedValue(2)); JSHandle lwm(thread, lightWeightMap); JSAPILightWeightMap::Set(thread, lwm, key, value); EXPECT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSAPILightWeightMap::Get(thread, lwm, key)), value)); JSHandle key1(thread, JSTaggedValue(2)); JSHandle value1(thread, JSTaggedValue(3)); JSAPILightWeightMap::Set(thread, lwm, key1, value1); JSHandle key2(thread, JSTaggedValue(3)); JSHandle value2(thread, JSTaggedValue(4)); JSAPILightWeightMap::Set(thread, lwm, key2, value2); // test species JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); JSHandle key3 = env->GetSpeciesSymbol(); JSHandle value3(thread, JSTaggedValue(5)); JSAPILightWeightMap::Set(thread, lwm, key3, value3); JSHandle key4(thread, JSTaggedValue(10)); JSHandle value4(thread, JSTaggedValue(10)); JSAPILightWeightMap::Set(thread, lwm, key4, value4); EXPECT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSAPILightWeightMap::Get(thread, lwm, key4)), value4)); // change value on Existed key JSHandle value5(thread, JSTaggedValue(100)); JSAPILightWeightMap::Set(thread, lwm, key4, value5); EXPECT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSAPILightWeightMap::Get(thread, lwm, key4)), value5)); EXPECT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSAPILightWeightMap::Get(thread, lwm, key)), value)); EXPECT_EQ(JSAPILightWeightMap::HasKey(thread, lwm, key1), JSTaggedValue::True()); EXPECT_EQ(JSAPILightWeightMap::HasKey(thread, lwm, key), JSTaggedValue::True()); EXPECT_EQ(JSAPILightWeightMap::HasKey(thread, lwm, key3), JSTaggedValue::True()); EXPECT_EQ(JSAPILightWeightMap::HasKey(thread, lwm, value), JSTaggedValue::True()); } HWTEST_F_L0(JSAPILightWeightMapTest, GetIndexOfKeyAndGetIndexOfValue) { constexpr uint32_t NODE_NUMBERS = 8; ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSMutableHandle value(thread, JSTaggedValue::Undefined()); std::string myKey("mykey"); std::string myValue("myvalue"); JSHandle lwm(thread, CreateLightWeightMap()); for (uint32_t i = 0; i < NODE_NUMBERS; i++) { std::string ikey = myKey + std::to_string(i); std::string ivalue = myValue + std::to_string(i); key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); JSAPILightWeightMap::Set(thread, lwm, key, value); EXPECT_TRUE(JSAPILightWeightMap::GetIndexOfKey(thread, lwm, key) != -1); EXPECT_TRUE(JSAPILightWeightMap::GetIndexOfValue(thread, lwm, value) != -1); uint32_t length = lwm->GetLength(); EXPECT_EQ(length, i + 1); } std::string ivalue = myValue + std::to_string(NODE_NUMBERS); value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); EXPECT_TRUE(JSAPILightWeightMap::GetIndexOfValue(thread, lwm, value) == -1); } HWTEST_F_L0(JSAPILightWeightMapTest, IsEmptyGetKeyAtGetValue) { JSHandle lwm(thread, CreateLightWeightMap()); JSHandle key(thread, JSTaggedValue(1)); JSHandle value(thread, JSTaggedValue(2)); JSAPILightWeightMap::Set(thread, lwm, key, value); JSHandle key1(thread, JSTaggedValue(2)); JSHandle value1(thread, JSTaggedValue(3)); JSAPILightWeightMap::Set(thread, lwm, key1, value1); JSHandle key2(thread, JSTaggedValue(3)); JSHandle value2(thread, JSTaggedValue(4)); JSAPILightWeightMap::Set(thread, lwm, key2, value2); JSHandle result = JSHandle(thread, JSAPILightWeightMap::GetValueAt(thread, lwm, 0)); EXPECT_TRUE(JSTaggedValue::Equal(thread, result, value)); result = JSHandle(thread, JSAPILightWeightMap::GetValueAt(thread, lwm, 1)); EXPECT_TRUE(JSTaggedValue::Equal(thread, result, value1)); result = JSHandle(thread, JSAPILightWeightMap::GetValueAt(thread, lwm, 2)); EXPECT_TRUE(JSTaggedValue::Equal(thread, result, value2)); result = JSHandle(thread, JSAPILightWeightMap::GetKeyAt(thread, lwm, 0)); EXPECT_TRUE(JSTaggedValue::Equal(thread, result, key)); result = JSHandle(thread, JSAPILightWeightMap::GetKeyAt(thread, lwm, 1)); EXPECT_TRUE(JSTaggedValue::Equal(thread, result, key1)); result = JSHandle(thread, JSAPILightWeightMap::GetKeyAt(thread, lwm, 2)); EXPECT_TRUE(JSTaggedValue::Equal(thread, result, key2)); EXPECT_EQ(lwm->IsEmpty(), JSTaggedValue::False()); JSAPILightWeightMap::Clear(thread, lwm); EXPECT_EQ(lwm->IsEmpty(), JSTaggedValue::True()); } HWTEST_F_L0(JSAPILightWeightMapTest, Remove) { JSHandle lwm(thread, CreateLightWeightMap()); JSHandle valueArray(thread, JSTaggedValue(TaggedArray::Cast(lwm->GetValues().GetTaggedObject()))); JSHandle key(thread, JSTaggedValue(1)); JSHandle value(thread, JSTaggedValue(2)); JSAPILightWeightMap::Set(thread, lwm, key, value); JSHandle key1(thread, JSTaggedValue(2)); JSHandle value1(thread, JSTaggedValue(3)); JSAPILightWeightMap::Set(thread, lwm, key1, value1); JSHandle key2(thread, JSTaggedValue(3)); JSHandle value2(thread, JSTaggedValue(4)); JSAPILightWeightMap::Set(thread, lwm, key2, value2); JSHandle key3(thread, JSTaggedValue(4)); JSHandle result = JSHandle(thread, JSAPILightWeightMap::Remove(thread, lwm, key2)); JSHandle resultNoExist = JSHandle(thread, JSAPILightWeightMap::Remove(thread, lwm, key3)); EXPECT_TRUE(JSTaggedValue::Equal(thread, result, value2)); bool isKeyExist = true; if (resultNoExist->IsUndefined()) { isKeyExist = false; } EXPECT_FALSE(isKeyExist); } HWTEST_F_L0(JSAPILightWeightMapTest, RemoveAt) { JSHandle lwm(thread, CreateLightWeightMap()); JSHandle valueArray(thread, JSTaggedValue(TaggedArray::Cast(lwm->GetValues().GetTaggedObject()))); JSHandle key(thread, JSTaggedValue(1)); JSHandle value(thread, JSTaggedValue(2)); JSAPILightWeightMap::Set(thread, lwm, key, value); JSHandle key1(thread, JSTaggedValue(2)); JSHandle value1(thread, JSTaggedValue(3)); JSAPILightWeightMap::Set(thread, lwm, key1, value1); JSHandle key2(thread, JSTaggedValue(3)); JSHandle value2(thread, JSTaggedValue(4)); JSAPILightWeightMap::Set(thread, lwm, key2, value2); int32_t removeIndex = JSAPILightWeightMap::GetIndexOfKey(thread, lwm, key1); EXPECT_EQ(JSAPILightWeightMap::RemoveAt(thread, lwm, removeIndex), JSTaggedValue::True()); JSHandle result(thread, JSAPILightWeightMap::Get(thread, lwm, key1)); bool isSuccessRemove = false; if (result->IsUndefined()) { isSuccessRemove = true; } EXPECT_TRUE(isSuccessRemove); EXPECT_EQ(JSAPILightWeightMap::HasValue(thread, lwm, value1), JSTaggedValue::False()); EXPECT_TRUE(lwm->GetLength() == 2); EXPECT_EQ(JSAPILightWeightMap::RemoveAt(thread, lwm, -1), JSTaggedValue::False()); EXPECT_EQ(JSAPILightWeightMap::RemoveAt(thread, lwm, 10), JSTaggedValue::False()); } HWTEST_F_L0(JSAPILightWeightMapTest, SetValueAt) { JSHandle lwm(thread, CreateLightWeightMap()); JSHandle key(thread, JSTaggedValue(1)); JSHandle value(thread, JSTaggedValue(2)); JSAPILightWeightMap::Set(thread, lwm, key, value); EXPECT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSAPILightWeightMap::Get(thread, lwm, key)), value)); JSHandle key1(thread, JSTaggedValue(2)); JSHandle value1(thread, JSTaggedValue(3)); JSAPILightWeightMap::Set(thread, lwm, key1, value1); JSHandle key2(thread, JSTaggedValue(3)); JSHandle value2(thread, JSTaggedValue(4)); JSAPILightWeightMap::Set(thread, lwm, key2, value2); JSHandle value3(thread, JSTaggedValue(5)); int32_t index = JSAPILightWeightMap::GetIndexOfKey(thread, lwm, key); JSAPILightWeightMap::SetValueAt(thread, lwm, index, value3); EXPECT_TRUE(JSTaggedValue::Equal(thread, JSHandle(thread, JSAPILightWeightMap::Get(thread, lwm, key)), value3)); } HWTEST_F_L0(JSAPILightWeightMapTest, GetStateOfKey) { JSHandle lwm(thread, CreateLightWeightMap()); JSHandle key1(thread, JSTaggedValue(1)); JSHandle value1(thread, JSTaggedValue(1)); JSAPILightWeightMap::Set(thread, lwm, key1, value1); KeyState keyState1 = JSAPILightWeightMap::GetStateOfKey(thread, lwm, key1); EXPECT_TRUE(keyState1.existed); JSHandle key2(thread, JSTaggedValue(2)); KeyState keyState2 = JSAPILightWeightMap::GetStateOfKey(thread, lwm, key2); EXPECT_FALSE(keyState2.existed); // hash Collision std::vector setVector = {0.0, 1224.0, 1285.0, 1463.0, 4307.0, 5135.0, 5903.0, 6603.0, 6780.0, 8416.0, 9401.0, 9740.0}; for (uint32_t i = 0; i < setVector.size() - 1; i++) { JSHandle key3(thread, JSTaggedValue(setVector[i])); JSHandle value3(thread, JSTaggedValue(setVector[i])); JSAPILightWeightMap::Set(thread, lwm, key3, value3); } // check for (uint32_t i = 0; i < setVector.size() - 1; i++) { JSHandle key4(thread, JSTaggedValue(setVector[i])); KeyState keyState4 = JSAPILightWeightMap::GetStateOfKey(thread, lwm, key4); EXPECT_TRUE(keyState4.existed); } JSHandle key5(thread, JSTaggedValue(setVector[setVector.size() - 1])); KeyState keyState5 = JSAPILightWeightMap::GetStateOfKey(thread, lwm, key5); EXPECT_FALSE(keyState5.existed); JSHandle key6(thread, JSTaggedValue(0)); KeyState keyState6 = JSAPILightWeightMap::GetStateOfKey(thread, lwm, key6); EXPECT_TRUE(keyState6.existed); } HWTEST_F_L0(JSAPILightWeightMapTest, IncreaseCapacityTo) { constexpr uint32_t NODE_NUMBERS = 10; ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSMutableHandle value(thread, JSTaggedValue::Undefined()); std::string myKey("mykey"); std::string myValue("myvalue"); JSHandle lwm(thread, CreateLightWeightMap()); for (uint32_t i = 0; i < NODE_NUMBERS; i++) { std::string ikey = myKey + std::to_string(i); std::string ivalue = myValue + std::to_string(i); key.Update(factory->NewFromStdString(ikey).GetTaggedValue()); value.Update(factory->NewFromStdString(ivalue).GetTaggedValue()); JSAPILightWeightMap::Set(thread, lwm, key, value); EXPECT_TRUE(JSAPILightWeightMap::GetIndexOfKey(thread, lwm, key) != -1); EXPECT_TRUE(JSAPILightWeightMap::GetIndexOfValue(thread, lwm, value) != -1); uint32_t length = lwm->GetLength(); EXPECT_EQ(length, i + 1); } EXPECT_EQ(JSAPILightWeightMap::IncreaseCapacityTo(thread, lwm, 15), JSTaggedValue::True()); EXPECT_EQ(JSAPILightWeightMap::IncreaseCapacityTo(thread, lwm, 9), JSTaggedValue::False()); } HWTEST_F_L0(JSAPILightWeightMapTest, Iterator) { constexpr uint32_t NODE_NUMBERS = 8; ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle lwm(thread, CreateLightWeightMap()); JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSMutableHandle value(thread, JSTaggedValue::Undefined()); for (uint32_t i = 0; i < NODE_NUMBERS; i++) { key.Update(JSTaggedValue(i)); value.Update(JSTaggedValue(i + 1)); JSAPILightWeightMap::Set(thread, lwm, key, value); } // test key or value JSHandle keyIter(factory->NewJSAPILightWeightMapIterator(lwm, IterationKind::KEY)); JSHandle valueIter(factory->NewJSAPILightWeightMapIterator(lwm, IterationKind::VALUE)); JSMutableHandle keyIterResult(thread, JSTaggedValue::Undefined()); JSMutableHandle valueIterResult(thread, JSTaggedValue::Undefined()); JSMutableHandle keyHandle(thread, JSTaggedValue::Undefined()); for (uint32_t i = 0; i < NODE_NUMBERS; i++) { keyIterResult.Update(JSIterator::IteratorStep(thread, keyIter).GetTaggedValue()); valueIterResult.Update(JSIterator::IteratorStep(thread, valueIter).GetTaggedValue()); JSTaggedValue k = JSIterator::IteratorValue(thread, keyIterResult).GetTaggedValue(); keyHandle.Update(k); JSTaggedValue v = JSIterator::IteratorValue(thread, valueIterResult).GetTaggedValue(); EXPECT_EQ(JSAPILightWeightMap::HasKey(thread, lwm, keyHandle), JSTaggedValue::True()); EXPECT_EQ(JSAPILightWeightMap::Get(thread, lwm, keyHandle), v); } // test key and value for (uint32_t i = 0; i < NODE_NUMBERS; i++) { JSTaggedValue k = JSTaggedValue(i); JSTaggedValue v = JSTaggedValue(i + 1); keyHandle.Update(k); EXPECT_EQ(JSAPILightWeightMap::HasKey(thread, lwm, keyHandle), JSTaggedValue::True()); EXPECT_EQ(JSAPILightWeightMap::Get(thread, lwm, keyHandle), v); } } HWTEST_F_L0(JSAPILightWeightMapTest, IsEmptyHasValueHasAll) { constexpr uint32_t NODE_NUMBERS = 8; ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle lwp(thread, CreateLightWeightMap()); JSHandle hasAllLwp(thread, CreateLightWeightMap()); JSMutableHandle key1(thread, JSTaggedValue::Undefined()); JSMutableHandle value1(thread, JSTaggedValue::Undefined()); JSMutableHandle value2(thread, JSTaggedValue::Undefined()); JSMutableHandle value3(thread, JSTaggedValue::Undefined()); std::string tValue; // test IsEmpty EXPECT_EQ(lwp->IsEmpty(), JSTaggedValue::True()); // test Set std::string myKey1("mykey"); std::string myValue1("myvalue"); for (uint32_t i = 0; i < NODE_NUMBERS; i++) { std::string iKey1 = myKey1 + std::to_string(i); std::string iValue1 = myValue1 + std::to_string(i + 1); key1.Update(factory->NewFromStdString(iKey1).GetTaggedValue()); value1.Update(factory->NewFromStdString(iValue1).GetTaggedValue()); JSAPILightWeightMap::Set(thread, lwp, key1, value1); } EXPECT_EQ(lwp->GetLength(), NODE_NUMBERS); // test HasValue for (uint32_t i = 0; i < NODE_NUMBERS; i++) { tValue = myValue1 + std::to_string(i + 1); value1.Update(factory->NewFromStdString(tValue).GetTaggedValue()); EXPECT_EQ(JSAPILightWeightMap::HasValue(thread, lwp, value1), JSTaggedValue::True()); } tValue = myValue1 + std::to_string(NODE_NUMBERS + 1); value1.Update(factory->NewFromStdString(tValue).GetTaggedValue()); EXPECT_EQ(JSAPILightWeightMap::HasValue(thread, lwp, value1), JSTaggedValue::False()); // test HasAll for (uint32_t i = 0; i < NODE_NUMBERS - 5; i++) { if (i == 1) { std::string mykey2("destKey"); std::string myValue2("destValue"); std::string iKey2 = mykey2 + std::to_string(i); std::string iValue2 = myValue2 + std::to_string(i); key1.Update(factory->NewFromStdString(iKey2).GetTaggedValue()); value1.Update(factory->NewFromStdString(iValue2).GetTaggedValue()); JSAPILightWeightMap::Set(thread, hasAllLwp, key1, value1); } else { std::string iKey = myKey1 + std::to_string(i); std::string iValue = myValue1 + std::to_string(i + 1); key1.Update(factory->NewFromStdString(iValue).GetTaggedValue()); value1.Update(factory->NewFromStdString(iValue).GetTaggedValue()); JSAPILightWeightMap::Set(thread, hasAllLwp, key1, value2); } } EXPECT_EQ(hasAllLwp->GetLength(), NODE_NUMBERS - 5); EXPECT_EQ(JSAPILightWeightMap::HasAll(thread, lwp, hasAllLwp), JSTaggedValue::False()); EXPECT_EQ(JSAPILightWeightMap::HasAll(thread, hasAllLwp, lwp), JSTaggedValue::False()); } /** * @tc.name: GetIteratorObj * @tc.desc: * @tc.type: FUNC * @tc.require: */ HWTEST_F_L0(JSAPILightWeightMapTest, GetIteratorObj) { JSHandle lwp(thread, CreateLightWeightMap()); JSHandle iteratorObj(thread, JSAPILightWeightMap::GetIteratorObj( thread, lwp, IterationKind::KEY_AND_VALUE)); EXPECT_TRUE(iteratorObj->IsJSAPILightWeightMapIterator()); } } // namespace panda::test