* Copyright (c) 2021 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/builtins/builtins_collator.h"
#include "ecmascript/builtins/builtins_array.h"
#include "ecmascript/global_env.h"
#include "ecmascript/js_collator.h"
#include "ecmascript/tests/test_helper.h"
using namespace panda::ecmascript;
using namespace panda::ecmascript::builtins;
namespace panda::test {
using BuiltinsArray = ecmascript::builtins::BuiltinsArray;
class BuiltinsCollatorTest : public BaseTestWithScope<true> {
};
static JSTaggedValue CollatorConstructor(JSThread *thread, std::vector<JSTaggedValue>& args,
JSHandle<JSFunction>& target)
{
auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*target), 8);
ecmaRuntimeCallInfo->SetFunction(target.GetTaggedValue());
ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
for (size_t i = 0; i < args.size(); i++) {
ecmaRuntimeCallInfo->SetCallArg(i, args[i]);
}
auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
JSTaggedValue result = BuiltinsCollator::CollatorConstructor(ecmaRuntimeCallInfo);
TestHelper::TearDownFrame(thread, prev);
return result;
}
enum class AlgorithmType {
COLLATOR_SUPPORTED_LOCALES_OF,
COLLATOR_RESOLVED_OPTIONS,
COLLATOR_COMPARE,
ARRAY_SORT,
ARRAY_JOIN,
ARRAY_TOSTR,
};
static JSTaggedValue CollatorAlgorithm(JSThread *thread, std::vector<JSTaggedValue>& args, int32_t maxArgLen,
AlgorithmType type, JSTaggedValue thisValue = JSTaggedValue::Undefined())
{
auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), maxArgLen);
ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
ecmaRuntimeCallInfo->SetThis(thisValue);
for (size_t i = 0; i < args.size(); i++) {
ecmaRuntimeCallInfo->SetCallArg(i, args[i]);
}
auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
JSTaggedValue result;
switch (type) {
case AlgorithmType::COLLATOR_SUPPORTED_LOCALES_OF:
result = BuiltinsCollator::SupportedLocalesOf(ecmaRuntimeCallInfo);
break;
case AlgorithmType::COLLATOR_RESOLVED_OPTIONS:
result = BuiltinsCollator::ResolvedOptions(ecmaRuntimeCallInfo);
break;
case AlgorithmType::COLLATOR_COMPARE:
result = BuiltinsCollator::Compare(ecmaRuntimeCallInfo);
break;
case AlgorithmType::ARRAY_SORT:
result = BuiltinsArray::Sort(ecmaRuntimeCallInfo);
break;
case AlgorithmType::ARRAY_JOIN:
result = BuiltinsArray::Join(ecmaRuntimeCallInfo);
break;
case AlgorithmType::ARRAY_TOSTR:
result = BuiltinsArray::ToString(ecmaRuntimeCallInfo);
break;
default:
break;
}
TestHelper::TearDownFrame(thread, prev);
return result;
}
HWTEST_F_L0(BuiltinsCollatorTest, CollatorConstructor)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSFunction> newTarget(env->GetCollatorFunction());
JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
JSHandle<JSTaggedValue> usageKey = thread->GlobalConstants()->GetHandledUsageString();
JSHandle<JSTaggedValue> localeMatcherKey = thread->GlobalConstants()->GetHandledLocaleMatcherString();
JSHandle<JSTaggedValue> numericKey = thread->GlobalConstants()->GetHandledNumericString();
JSHandle<JSTaggedValue> caseFirstKey = thread->GlobalConstants()->GetHandledCaseFirstString();
JSHandle<JSTaggedValue> sensitivityKey = thread->GlobalConstants()->GetHandledSensitivityString();
JSHandle<JSTaggedValue> ignorePunctuationKey = thread->GlobalConstants()->GetHandledIgnorePunctuationString();
JSHandle<JSTaggedValue> usageValue(factory->NewFromASCII("search"));
JSHandle<JSTaggedValue> localeMatcherValue(factory->NewFromASCII("lookup"));
JSHandle<JSTaggedValue> numericValue(factory->NewFromASCII("true"));
JSHandle<JSTaggedValue> caseFirstValue(factory->NewFromASCII("lower"));
JSHandle<JSTaggedValue> sensitivityValue(factory->NewFromASCII("variant"));
JSHandle<JSTaggedValue> ignorePunctuationValue(factory->NewFromASCII("true"));
JSHandle<JSTaggedValue> localesString(factory->NewFromASCII("en-Latn-US"));
JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
JSObject::SetProperty(thread, optionsObj, usageKey, usageValue);
JSObject::SetProperty(thread, optionsObj, localeMatcherKey, localeMatcherValue);
JSObject::SetProperty(thread, optionsObj, caseFirstKey, caseFirstValue);
JSObject::SetProperty(thread, optionsObj, sensitivityKey, sensitivityValue);
JSObject::SetProperty(thread, optionsObj, ignorePunctuationKey, ignorePunctuationValue);
JSObject::SetProperty(thread, optionsObj, numericKey, numericValue);
std::vector<JSTaggedValue> vals{localesString.GetTaggedValue(), optionsObj.GetTaggedValue()};
auto result = CollatorConstructor(thread, vals, newTarget);
EXPECT_TRUE(result.IsJSCollator());
}
static JSTaggedValue JSCollatorCreateWithLocaleTest(JSThread *thread, JSHandle<JSTaggedValue> &locale)
{
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSFunction> newTarget(env->GetCollatorFunction());
JSHandle<JSTaggedValue> localesString = locale;
std::vector<JSTaggedValue> vals{localesString.GetTaggedValue(), JSTaggedValue::Undefined()};
auto result = CollatorConstructor(thread, vals, newTarget);
EXPECT_TRUE(result.IsJSCollator());
return result;
}
static JSTaggedValue JSCollatorCreateWithLocaleAndOptionsTest(JSThread *thread, JSHandle<JSTaggedValue> &locale)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSFunction> newTarget(env->GetCollatorFunction());
JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
JSHandle<JSTaggedValue> localesString = locale;
JSHandle<JSTaggedValue> usageKey = thread->GlobalConstants()->GetHandledUsageString();
JSHandle<JSTaggedValue> sensitivityKey = thread->GlobalConstants()->GetHandledSensitivityString();
JSHandle<JSTaggedValue> usageValue(factory->NewFromASCII("search"));
JSHandle<JSTaggedValue> sensitivityValue(factory->NewFromASCII("base"));
JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
JSObject::SetProperty(thread, optionsObj, usageKey, usageValue);
JSObject::SetProperty(thread, optionsObj, sensitivityKey, sensitivityValue);
std::vector<JSTaggedValue> vals{localesString.GetTaggedValue(), optionsObj.GetTaggedValue()};
auto result = CollatorConstructor(thread, vals, newTarget);
EXPECT_TRUE(result.IsJSCollator());
return result;
}
HWTEST_F_L0(BuiltinsCollatorTest, Compare_001)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSTaggedValue> locale(factory->NewFromASCII("de"));
JSHandle<JSCollator> jsCollator = JSHandle<JSCollator>(thread, JSCollatorCreateWithLocaleTest(thread, locale));
std::vector<JSTaggedValue> vals{};
auto result1 = CollatorAlgorithm(thread, vals, 4, AlgorithmType::COLLATOR_COMPARE, jsCollator.GetTaggedValue());
JSHandle<JSFunction> jsFunction(thread, result1);
JSArray *jsArray =
JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
JSHandle<JSObject> jsObject(thread, jsArray);
JSHandle<JSTaggedValue> key0(thread, JSTaggedValue(0));
JSHandle<JSTaggedValue> value0(factory->NewFromASCII("Z"));
PropertyDescriptor desc0(thread, value0, true, true, true);
JSArray::DefineOwnProperty(thread, jsObject, key0, desc0);
JSHandle<JSTaggedValue> key1(thread, JSTaggedValue(1));
JSHandle<JSTaggedValue> value1(factory->NewFromASCII("a"));
PropertyDescriptor desc1(thread, value1, true, true, true);
JSArray::DefineOwnProperty(thread, jsObject, key1, desc1);
JSHandle<JSTaggedValue> key2(thread, JSTaggedValue(2));
JSHandle<JSTaggedValue> value2(factory->NewFromUtf8("ä"));
PropertyDescriptor desc2(thread, value2, true, true, true);
JSArray::DefineOwnProperty(thread, jsObject, key2, desc2);
std::vector<JSTaggedValue> arrayVals{jsFunction.GetTaggedValue()};
auto result2 = CollatorAlgorithm(thread, arrayVals, 6, AlgorithmType::ARRAY_SORT, jsObject.GetTaggedValue());
JSHandle<JSTaggedValue> resultArr =
JSHandle<JSTaggedValue>(thread, JSTaggedValue(static_cast<JSTaggedType>(result2.GetRawData())));
EXPECT_EQ(JSTaggedValue::SameValue(thread, JSArray::GetProperty(thread, resultArr, key0).GetValue(), value1), true);
EXPECT_EQ(JSTaggedValue::SameValue(thread, JSArray::GetProperty(thread, resultArr, key1).GetValue(), value2), true);
EXPECT_EQ(JSTaggedValue::SameValue(thread, JSArray::GetProperty(thread, resultArr, key2).GetValue(), value0), true);
}
HWTEST_F_L0(BuiltinsCollatorTest, Compare_002)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSTaggedValue> locale(factory->NewFromASCII("sv"));
JSHandle<JSCollator> jsCollator = JSHandle<JSCollator>(thread, JSCollatorCreateWithLocaleTest(thread, locale));
std::vector<JSTaggedValue> vals{};
auto result1 = CollatorAlgorithm(thread, vals, 4, AlgorithmType::COLLATOR_COMPARE, jsCollator.GetTaggedValue());
JSHandle<JSFunction> jsFunction(thread, result1);
JSArray *jsArray =
JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
JSHandle<JSObject> jsObject(thread, jsArray);
JSHandle<JSTaggedValue> key0(thread, JSTaggedValue(0));
JSHandle<JSTaggedValue> value0(factory->NewFromASCII("Z"));
PropertyDescriptor desc0(thread, value0, true, true, true);
JSArray::DefineOwnProperty(thread, jsObject, key0, desc0);
JSHandle<JSTaggedValue> key1(thread, JSTaggedValue(1));
JSHandle<JSTaggedValue> value1(factory->NewFromASCII("a"));
PropertyDescriptor desc1(thread, value1, true, true, true);
JSArray::DefineOwnProperty(thread, jsObject, key1, desc1);
JSHandle<JSTaggedValue> key2(thread, JSTaggedValue(2));
JSHandle<JSTaggedValue> value2(factory->NewFromUtf8("ä"));
PropertyDescriptor desc2(thread, value2, true, true, true);
JSArray::DefineOwnProperty(thread, jsObject, key2, desc2);
std::vector<JSTaggedValue> arrayVals{jsFunction.GetTaggedValue()};
auto result2 = CollatorAlgorithm(thread, arrayVals, 6, AlgorithmType::ARRAY_SORT, jsObject.GetTaggedValue());
JSHandle<JSObject> resultObj(thread, result2);
JSHandle<EcmaString> str = thread->GetEcmaVM()->GetFactory()->NewFromUtf8("a,Z,ä");
std::vector<JSTaggedValue> arrayVals2{};
auto result = CollatorAlgorithm(thread, arrayVals2, 4, AlgorithmType::ARRAY_JOIN, resultObj.GetTaggedValue());
JSHandle<EcmaString> resultHandle(thread, reinterpret_cast<EcmaString *>(result.GetRawData()));
EXPECT_EQ(EcmaStringAccessor::Compare(instance, resultHandle, str), 0);
}
HWTEST_F_L0(BuiltinsCollatorTest, Compare_003)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<JSTaggedValue> locale(factory->NewFromASCII("sv"));
JSHandle<JSCollator> jsCollator =
JSHandle<JSCollator>(thread, JSCollatorCreateWithLocaleAndOptionsTest(thread, locale));
std::vector<JSTaggedValue> vals{};
auto result1 = CollatorAlgorithm(thread, vals, 4, AlgorithmType::COLLATOR_COMPARE, jsCollator.GetTaggedValue());
JSHandle<JSFunction> jsFunction(thread, result1);
JSArray *jsArray =
JSArray::Cast(JSArray::ArrayCreate(thread, JSTaggedNumber(0)).GetTaggedValue().GetTaggedObject());
JSHandle<JSObject> jsObject(thread, jsArray);
JSHandle<JSTaggedValue> value0(factory->NewFromUtf8("Congrès"));
JSHandle<JSTaggedValue> value1(factory->NewFromASCII("congres"));
PropertyDescriptor desc(thread, JSHandle<JSTaggedValue>(jsFunction), true, true, true);
JSHandle<JSTaggedValue> joinKey(factory->NewFromASCII("join"));
JSArray::DefineOwnProperty(thread, jsObject, joinKey, desc);
std::vector<JSTaggedValue> arrayVals{value0.GetTaggedValue(), value1.GetTaggedValue()};
auto result2 = CollatorAlgorithm(thread, arrayVals, 8, AlgorithmType::ARRAY_TOSTR, jsObject.GetTaggedValue());
JSHandle<JSTaggedValue> resultHandle(thread, result2);
EXPECT_EQ(resultHandle->GetInt(), 0);
}
HWTEST_F_L0(BuiltinsCollatorTest, ResolvedOptions)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
auto globalConst = thread->GlobalConstants();
JSHandle<JSTaggedValue> locale(factory->NewFromASCII("de"));
JSHandle<JSCollator> jsCollator = JSHandle<JSCollator>(thread, JSCollatorCreateWithLocaleTest(thread, locale));
std::vector<JSTaggedValue> vals{};
auto result =
CollatorAlgorithm(thread, vals, 4, AlgorithmType::COLLATOR_RESOLVED_OPTIONS, jsCollator.GetTaggedValue());
JSHandle<JSTaggedValue> resultObj =
JSHandle<JSTaggedValue>(thread, JSTaggedValue(static_cast<JSTaggedType>(result.GetRawData())));
JSHandle<JSTaggedValue> localeKey = globalConst->GetHandledLocaleString();
EXPECT_EQ(JSTaggedValue::SameValue(thread, JSObject::GetProperty(thread, resultObj, localeKey).GetValue(), locale),
true);
JSHandle<JSTaggedValue> usageKey = globalConst->GetHandledUsageString();
JSHandle<JSTaggedValue> defaultUsageValue(factory->NewFromASCII("sort"));
EXPECT_EQ(JSTaggedValue::SameValue(thread, JSObject::GetProperty(thread, resultObj, usageKey).GetValue(),
defaultUsageValue),
true);
JSHandle<JSTaggedValue> handledCaseFirstKey = globalConst->GetHandledCaseFirstString();
JSHandle<JSTaggedValue> handledCaseFirstValue(factory->NewFromASCII("upper"));
EXPECT_EQ(JSTaggedValue::SameValue(thread, JSObject::GetProperty(thread, resultObj, handledCaseFirstKey).GetValue(),
handledCaseFirstValue),
true);
}
HWTEST_F_L0(BuiltinsCollatorTest, SupportedLocalesOf)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
JSHandle<JSTaggedValue> localeMatcherKey = thread->GlobalConstants()->GetHandledLocaleMatcherString();
JSHandle<JSTaggedValue> localeMatcherValue(factory->NewFromASCII("lookup"));
JSHandle<JSObject> optionsObj = factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
JSObject::SetProperty(thread, optionsObj, localeMatcherKey, localeMatcherValue);
JSHandle<JSTaggedValue> locale(factory->NewFromASCII("id-u-co-pinyin-de-ID"));
std::vector<JSTaggedValue> vals{locale.GetTaggedValue(), optionsObj.GetTaggedValue()};
auto resultArr = CollatorAlgorithm(thread, vals, 8, AlgorithmType::COLLATOR_SUPPORTED_LOCALES_OF);
JSHandle<JSArray> resultHandle(thread, resultArr);
JSHandle<TaggedArray> elements(thread, resultHandle->GetElements(thread));
EXPECT_EQ(elements->GetLength(), 1U);
JSHandle<EcmaString> handleEcmaStr(thread, elements->Get(thread, 0));
EXPECT_STREQ("id-u-co-pinyin-de-id", EcmaStringAccessor(handleEcmaStr).ToCString(thread).c_str());
}
}