1 /*
2 * Copyright (c) 2023 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 <gtest/gtest.h>
17 #include <unicode/ubidi.h>
18
19 #include "mock/mock_any_span.h"
20 #include "param_test_macros.h"
21 #include "texgine_exception.h"
22 #include "text_span.h"
23 #include "texgine/typography_types.h"
24 #include "text_converter.h"
25 #include "text_merger.h"
26 #include "my_any_span.h"
27
28 using namespace testing;
29 using namespace testing::ext;
30
31 namespace OHOS {
32 namespace Rosen {
33 namespace TextEngine {
34 int g_tempCount = 0;
35
36 std::vector<int32_t> tempIdeographic = {0};
u_getIntPropertyValue(UChar32 c,UProperty which)37 U_STABLE int32_t U_EXPORT2 u_getIntPropertyValue(UChar32 c, UProperty which)
38 {
39 auto ret = tempIdeographic[g_tempCount];
40 return ret;
41 }
42
43 std::vector<UBool> isWhitespace = {0};
u_isWhitespace(UChar32 c)44 U_STABLE UBool U_EXPORT2 u_isWhitespace(UChar32 c)
45 {
46 auto ret = isWhitespace[g_tempCount];
47 g_tempCount++;
48 return ret;
49 }
50
51 struct TextSpanInfo {
52 bool rtl = false;
53 CharGroups cgs = CharGroups::CreateEmpty();
54 };
55
56 class ControllerForTest {
57 public:
GenerateTestSpan(TextSpanInfo info)58 static std::shared_ptr<TextSpan> GenerateTestSpan(TextSpanInfo info)
59 {
60 auto ts = std::make_shared<TextSpan>();
61 ts->rtl_ = info.rtl;
62 ts->cgs_ = info.cgs;
63 return ts;
64 }
65
GetCharGroups(const std::shared_ptr<TextSpan> & span)66 static CharGroups GetCharGroups(const std::shared_ptr<TextSpan> &span)
67 {
68 return span->cgs_;
69 }
70 };
71
GenTestSpan(TextSpanInfo info)72 auto GenTestSpan(TextSpanInfo info)
73 {
74 return ControllerForTest::GenerateTestSpan(info);
75 }
76
InitMockArgs(std::vector<int32_t> ideographics,std::vector<UBool> whitespaces,int count=0)77 auto InitMockArgs(std::vector<int32_t> ideographics, std::vector<UBool> whitespaces, int count = 0)
78 {
79 return [ideographics, whitespaces, count]() {
80 g_tempCount = count;
81 tempIdeographic = ideographics;
82 isWhitespace = whitespaces;
83 };
84 }
85
GetMergerResultChecker(const MergeResult result,const CharGroups & spanCgs={},bool isMerged=false,IndexRange range={})86 auto GetMergerResultChecker(const MergeResult result, const CharGroups &spanCgs = {},
87 bool isMerged = false, IndexRange range = {})
88 {
89 return [result, spanCgs, isMerged, range](MergeResult &&actual, const VariantSpan &span,
__anonb12e75ec0202(MergeResult &&actual, const VariantSpan &span, std::optional<bool> &rtl, CharGroups &cgs, TextMerger &object) 90 std::optional<bool> &rtl, CharGroups &cgs, TextMerger &object) {
91 if (result != actual) {
92 return false;
93 }
94 if (result != MergeResult::IGNORE && result != MergeResult::REJECTED) {
95 if (isMerged) {
96 return cgs.GetRange() == range;
97 } else {
98 return spanCgs == cgs;
99 }
100 }
101 return true;
102 };
103 }
104
105 struct MergedSpanCheck {
106 IndexRange range_ = {0, 0};
107 bool isAnySpan_ = false;
108 };
109
MergedSpanChecker(const std::vector<struct MergedSpanCheck> & result)110 auto MergedSpanChecker(const std::vector<struct MergedSpanCheck> &result)
111 {
112 return [result](std::vector<VariantSpan> sss) {
113 if (result.size() != sss.size()) {
114 return false;
115 }
116 for (auto i = 0u; i < sss.size(); i++) {
117 if (result[i].isAnySpan_) {
118 if (sss[i].TryToAnySpan() == nullptr) {
119 return false;
120 }
121 } else {
122 auto ts = sss[i].TryToTextSpan();
123 if (ts == nullptr) {
124 return false;
125 }
126 if (!(ControllerForTest::GetCharGroups(ts).GetRange() == result[i].range_)) {
127 return false;
128 }
129 }
130 }
131 return true;
132 };
133 }
134
135 #define PARAMCLASS TextMerger
136 class TextMergerTest : public testing::Test {
137 public:
SetUpTestCase()138 static void SetUpTestCase()
139 {
140 // {0x013B, 13.664}: {glyph codepoint, glyph advanceX}
141 cgs1_.PushBack({.chars = TextConverter::ToUTF16("m"), .glyphs = {{0x013B, 13.664}}});
142 cgs1_.PushBack({.chars = TextConverter::ToUTF16("o"), .glyphs = {{0x0145, 9.456}}});
143 cgs1_.PushBack({.chars = TextConverter::ToUTF16("s"), .glyphs = {{0x0166, 7.28}}});
144 cgs1_.PushBack({.chars = TextConverter::ToUTF16("t"), .glyphs = {{0x016E, 5.88}}});
145 cgs1_.PushBack({.chars = TextConverter::ToUTF16(" "), .glyphs = {{0x0002, 4.32}}});
146
147 cgs2_.PushBack({.chars = TextConverter::ToUTF16("m"), .glyphs = {{0x013B, 13.664}}});
148 cgs2_.PushBack({.chars = TextConverter::ToUTF16("o"), .glyphs = {{0x0145, 9.456}}});
149
150 tsCgs1_ = GenTestSpan({.rtl = false, .cgs = cgs1_});
151 // (1, 2): (sart, end)
152 tsSubCgs12_ = GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(1, 2)});
153
154 seqT_ = {GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(0, 1)})};
155
156 seqTT_ = { GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(0, 1)}),
157 GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(1, 2)}) };
158
159 seqTTST_ = { GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(0, 1)}),
160 GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(1, 5)}),
161 GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(0, 1)}) };
162
163 seqTiTi_ = { GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(0, 1)}),
164 GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(1, 2)}) };
165
166 seqTA_ = { GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(0, 1)}), std::make_shared<MyAnySpan>(0, 0) };
167
168 seqTTATT_ = { GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(0, 1)}),
169 GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(1, 2)}),
170 std::make_shared<MyAnySpan>(0, 0),
171 GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(2, 3)}),
172 GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(3, 4)}) };
173
174 seqRL_ = { GenTestSpan({.rtl = true, .cgs = cgs1_.GetSub(0, 1)}),
175 GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(1, 2)}) };
176
177 seqLR_ = { GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(0, 1)}),
178 GenTestSpan({.rtl = true, .cgs = cgs1_.GetSub(1, 2)}) };
179
180 seqRLR_ = { GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(0, 1)}),
181 GenTestSpan({.rtl = true, .cgs = cgs1_.GetSub(1, 2)}),
182 GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(2, 3)}) };
183
184 seqTTtt_ = { GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(0, 1)}),
185 GenTestSpan({.rtl = false, .cgs = cgs1_.GetSub(1, 2)}),
186 GenTestSpan({.rtl = false, .cgs = cgs2_.GetSub(0, 1)}),
187 GenTestSpan({.rtl = false, .cgs = cgs2_.GetSub(1, 2)}) };
188 }
189 static inline std::shared_ptr<TextSpan> tsCgs1_ = nullptr;
190 static inline std::shared_ptr<TextSpan> tsSubCgs12_ = nullptr;
191 static inline std::vector<VariantSpan> seqT_ = {};
192 static inline std::vector<VariantSpan> seqTT_ = {};
193 static inline std::vector<VariantSpan> seqTTST_ = {};
194 static inline std::vector<VariantSpan> seqTiTi_ = {};
195 static inline std::vector<VariantSpan> seqTA_ = {};
196 static inline std::vector<VariantSpan> seqTTATT_ = {};
197 static inline std::vector<VariantSpan> seqRL_ = {};
198 static inline std::vector<VariantSpan> seqLR_ = {};
199 static inline std::vector<VariantSpan> seqRLR_ = {};
200 static inline std::vector<VariantSpan> seqTTtt_ = {};
201
202 static inline CharGroups cgs1_ = CharGroups::CreateEmpty();
203 static inline CharGroups cgs2_ = CharGroups::CreateEmpty();
204 };
205
206 #define PARAMFUNC MergeSpan
207 /**
208 * @tc.name: MergeSpan
209 * @tc.desc: Verify the MergeSpan
210 * @tc.type:FUNC
211 */
212 HWTEST_F(TextMergerTest, MergeSpan, TestSize.Level1)
213 {
214 DEFINE_ALL_TESTINFO3(VariantSpan, std::optional<bool>, CharGroups);
215
216 std::shared_ptr<TextSpan> tsNull = nullptr;
217 std::shared_ptr<MyAnySpan> asNull = nullptr;
218
219 TextMerger tm;
220 RUN_ALL_TESTINFO3(tm, {.arg1 = tsNull, .arg2 = false, .arg3 = {}, .exception = ExceptionType::INVALID_ARGUMENT });
221 RUN_ALL_TESTINFO3(tm, {.arg1 = tsNull, .arg2 = false, .arg3 = {}, .exception = ExceptionType::INVALID_ARGUMENT });
222 RUN_ALL_TESTINFO3(tm, {.arg1 = asNull, .arg2 = false, .arg3 = {}, .exception = ExceptionType::INVALID_ARGUMENT });
223 // (0, 0): (width, height)
224 RUN_ALL_TESTINFO3(tm, {.arg1 = std::make_shared<MyAnySpan>(0, 0), .arg2 = false, .arg3 = CharGroups::CreateEmpty(),
225 .checkFunc = GetMergerResultChecker(MergeResult::REJECTED) });
226 RUN_ALL_TESTINFO3(tm, {.arg1 = std::make_shared<MyAnySpan>(0, 0), .arg2 = false, .arg3 = {},
227 .checkFunc = GetMergerResultChecker(MergeResult::IGNORE) });
228 RUN_ALL_TESTINFO3(tm, {.arg1 = GenTestSpan({.rtl = true, .cgs = {}}), .arg2 = false, .arg3 = {},
229 .checkFunc = GetMergerResultChecker(MergeResult::REJECTED) });
230 RUN_ALL_TESTINFO3(tm, {.arg1 = GenTestSpan({.rtl = false, .cgs = {}}), .arg2 = false, .arg3 = {},
231 .exception = ExceptionType::ERROR_STATUS });
232 RUN_ALL_TESTINFO3(tm, {.arg1 = GenTestSpan({.rtl = false, .cgs = CharGroups::CreateEmpty()}),
233 .arg2 = false, .arg3 = {}, .exception = ExceptionType::ERROR_STATUS });
234 RUN_ALL_TESTINFO3(tm, {.arg1 = GenTestSpan({.rtl = false, .cgs = cgs1_}),
235 .arg2 = false, .arg3 = CharGroups::CreateEmpty(), .checkFunc = GetMergerResultChecker(MergeResult::REJECTED) });
236 RUN_ALL_TESTINFO3(tm, {.init = InitMockArgs({1}, {0}), .arg1 = tsCgs1_, .arg2 = false, .arg3 = {},
237 .checkFunc = GetMergerResultChecker(MergeResult::ACCEPTED, ControllerForTest::GetCharGroups(tsCgs1_)) });
238 RUN_ALL_TESTINFO3(tm, {.init = InitMockArgs({0}, {1}), .arg1 = tsCgs1_, .arg2 = false, .arg3 = {},
239 .checkFunc = GetMergerResultChecker(MergeResult::BREAKED, ControllerForTest::GetCharGroups(tsCgs1_)) });
240 RUN_ALL_TESTINFO3(tm, {.init = InitMockArgs({0}, {0}), .arg1 = tsCgs1_, .arg2 = false, .arg3 = {},
241 .checkFunc = GetMergerResultChecker(MergeResult::ACCEPTED, ControllerForTest::GetCharGroups(tsCgs1_)) });
242 RUN_ALL_TESTINFO3(tm, {.init = InitMockArgs({1}, {0}), .arg1 = tsSubCgs12_,
243 .arg2 = false, .arg3 = cgs1_.GetSub(0, 1),
244 // {0, 2}: check the IndexRange
245 .checkFunc = GetMergerResultChecker(MergeResult::ACCEPTED, {}, true, {0, 2}) });
246 RUN_ALL_TESTINFO3(tm, {.init = InitMockArgs({0}, {1}), .arg1 = tsSubCgs12_,
247 .arg2 = false, .arg3 = cgs1_.GetSub(0, 1),
248 .checkFunc = GetMergerResultChecker(MergeResult::BREAKED, {}, true, {0, 2}) });
249 RUN_ALL_TESTINFO3(tm, {.init = InitMockArgs({0}, {0}), .arg1 = tsSubCgs12_,
250 .arg2 = false, .arg3 = cgs1_.GetSub(0, 1),
251 .checkFunc = GetMergerResultChecker(MergeResult::ACCEPTED, {}, true, {0, 2}) });
252 }
253 #undef PARAMFUNC
254
255 #define PARAMFUNC MergeSpans
256 /**
257 * @tc.name: MergeSpans
258 * @tc.desc: Verify the MergeSpans
259 * @tc.type:FUNC
260 */
261 HWTEST_F(TextMergerTest, MergeSpans, TestSize.Level1)
262 {
263 DEFINE_TESTINFO1(std::vector<VariantSpan>);
264
265 // A->AnySpan T/t->TextSpan S->Space L->LeftToRight R->RightToLeft
266 std::vector<VariantSpan> seqA = {std::make_shared<MyAnySpan>(0, 0)};
267 std::vector<VariantSpan> seqAA = {std::make_shared<MyAnySpan>(0, 0), std::make_shared<MyAnySpan>(0, 0)};
268 std::shared_ptr<TextSpan> tsNull = nullptr;
269 std::shared_ptr<MyAnySpan> asNull = nullptr;
270
271 TextMerger tm;
272 RUN_TESTINFO1(tm, { .arg1 = {tsNull}, .exception = ExceptionType::INVALID_ARGUMENT });
273 RUN_TESTINFO1(tm, { .arg1 = {asNull}, .exception = ExceptionType::INVALID_ARGUMENT });
274 RUN_TESTINFO1(tm, { .arg1 = {}, .checkFunc = CreateVecSizeChecker<VariantSpan>(0) });
275 RUN_TESTINFO1(tm, { .arg1 = seqA, .checkFunc = MergedSpanChecker({{.isAnySpan_ = true}}) });
276 RUN_TESTINFO1(tm, { .arg1 = seqAA, .checkFunc = MergedSpanChecker({{.isAnySpan_ = true}, {.isAnySpan_ = true}}) });
277 RUN_TESTINFO1(tm, { .init = InitMockArgs({0}, {0}), .arg1 = seqT_,
278 .checkFunc = MergedSpanChecker({{.range_ = {0, 1}}}) });
279 RUN_TESTINFO1(tm, { .init = InitMockArgs({0, 0}, {0, 0}), .arg1 = seqTT_,
280 .checkFunc = MergedSpanChecker({{.range_ = {0, 2}}}) });
281 RUN_TESTINFO1(tm, { .init = InitMockArgs({0, 0, 0}, {0, 1, 0}), .arg1 = seqTTST_,
282 .checkFunc = MergedSpanChecker({{.range_ = {0, 5}}, {.range_ = {0, 1}}}) });
283 RUN_TESTINFO1(tm, { .init = InitMockArgs({1, 1}, {0, 0}), .arg1 = seqTiTi_,
284 .checkFunc = MergedSpanChecker({{.range_ = {0, 2}}}) });
285 RUN_TESTINFO1(tm, { .init = InitMockArgs({0}, {0}), .arg1 = seqTA_,
286 .checkFunc = MergedSpanChecker({{.range_ = {0, 1}}, {.isAnySpan_ = true}}) });
287 RUN_TESTINFO1(tm, { .init = InitMockArgs({0, 0, 0, 0}, {0, 0, 0, 0}), .arg1 = seqTTATT_,
288 .checkFunc = MergedSpanChecker({{.range_ = {0, 2}}, {.isAnySpan_ = true}, {.range_ = {2, 4}}}) });
289 RUN_TESTINFO1(tm, { .init = InitMockArgs({0, 0}, {0, 0}), .arg1 = seqRL_,
290 .checkFunc = MergedSpanChecker({{.range_ = {0, 1}}, {.range_ = {1, 2}}}) });
291 RUN_TESTINFO1(tm, { .init = InitMockArgs({0, 0}, {0, 0}), .arg1 = seqLR_,
292 .checkFunc = MergedSpanChecker({{.range_ = {0, 1}}, {.range_ = {1, 2}}}) });
293 RUN_TESTINFO1(tm, { .init = InitMockArgs({0, 0, 0}, {0, 0, 0}), .arg1 = seqRLR_,
294 .checkFunc = MergedSpanChecker({{.range_ = {0, 1}}, {.range_ = {1, 2}}, {.range_ = {2, 3}}}) });
295 RUN_TESTINFO1(tm, { .init = InitMockArgs({0, 0, 0, 0}, {0, 0, 0, 0}), .arg1 = seqTTtt_,
296 .checkFunc = MergedSpanChecker({{.range_ = {0, 2}}, {.range_ = {0, 2}}}) });
297 }
298 #undef PARAMFUNC
299 } // namespace TextEngine
300 } // namespace Rosen
301 } // namespace OHOS
302