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 "bidi_processer.h"
17 #include "mock/mock_any_span.h"
18 #include "texgine_exception.h"
19 #include "text_breaker.h"
20 #include "text_converter.h"
21 #include "text_span.h"
22
23 #include <gtest/gtest.h>
24 #include <unicode/ubidi.h>
25
26 using namespace testing;
27 using namespace testing::ext;
28
29 struct UBiDi {};
30
31 namespace {
32 std::unique_ptr<UBiDi> g_bidi = nullptr;
33 std::vector<int> g_start;
34 std::vector<int> g_length;
35 } // namespace
36
37 U_STABLE UBiDi *U_EXPORT2
ubidi_open(void)38 ubidi_open(void)
39 {
40 return g_bidi.get();
41 }
42
43 U_STABLE void U_EXPORT2
ubidi_close(UBiDi * pBiDi)44 ubidi_close(UBiDi *pBiDi) {}
45
46 auto g_status = U_ZERO_ERROR;
47 U_STABLE void U_EXPORT2
ubidi_setPara(UBiDi * pBiDi,const UChar * text,int32_t length,UBiDiLevel paraLevel,UBiDiLevel * embeddingLevels,UErrorCode * pErrorCode)48 ubidi_setPara(UBiDi *pBiDi, const UChar *text, int32_t length,
49 UBiDiLevel paraLevel, UBiDiLevel *embeddingLevels,
50 UErrorCode *pErrorCode)
51 {
52 *pErrorCode = g_status;
53 }
54
55 int g_size = 0;
56 U_STABLE int32_t U_EXPORT2
ubidi_countRuns(UBiDi * pBiDi,UErrorCode * pErrorCode)57 ubidi_countRuns(UBiDi *pBiDi, UErrorCode *pErrorCode)
58 {
59 return g_size;
60 }
61
62 int g_count = 0;
63 UBiDiDirection g_rtl = UBIDI_RTL;
64 U_STABLE UBiDiDirection U_EXPORT2
ubidi_getVisualRun(UBiDi * pBiDi,int32_t runIndex,int32_t * pLogicalStart,int32_t * pLength)65 ubidi_getVisualRun(UBiDi *pBiDi, int32_t runIndex,
66 int32_t *pLogicalStart, int32_t *pLength)
67 {
68 assert(g_count < g_size);
69 *pLogicalStart = (g_start[g_count]);
70 *pLength = (g_length[g_count]);
71 g_count++;
72 return g_rtl;
73 }
74
75 struct Style {
76 int size = 1;
77 std::unique_ptr<UBiDi> bidi = std::make_unique<UBiDi>();
78 UErrorCode status = U_ZERO_ERROR;
79 UBiDiDirection rtl = UBIDI_RTL;
80 std::vector<int> start = {0};
81 std::vector<int> length = {4};
82 };
83
84 namespace {
InitMyMockVars(Style style)85 void InitMyMockVars(Style style)
86 {
87 g_count = 0;
88 g_size = style.size;
89 g_bidi = std::move(style.bidi);
90 g_status = style.status;
91 g_rtl = style.rtl;
92 g_start = style.start;
93 g_length = style.length;
94 }
95 } // namespace
96
97 namespace OHOS {
98 namespace Rosen {
99 namespace TextEngine {
100 class BidiProcesserTest : public testing::Test {
101 public:
SetUpTestCase()102 static void SetUpTestCase()
103 {
104 tpstyle_ = std::make_shared<TypographyStyle>();
105 cgs1_.PushBack({.chars = TextConverter::ToUTF16("m"), .glyphs = {{0x013B, 13.664}}});
106 cgs1_.PushBack({.chars = TextConverter::ToUTF16("o"), .glyphs = {{0x0145, 9.456}}});
107 cgs1_.PushBack({.chars = TextConverter::ToUTF16("s"), .glyphs = {{0x0166, 7.28}}});
108 cgs1_.PushBack({.chars = TextConverter::ToUTF16("t"), .glyphs = {{0x016E, 5.88}}});
109 }
110
111 static inline std::shared_ptr<TypographyStyle> tpstyle_ = nullptr;
112 static inline TextStyle textStyle_;
113 static inline CharGroups cgs1_ = CharGroups::CreateEmpty();
114 std::vector<VariantSpan> spans_;
115 BidiProcesser bp;
116 };
117
118 /**
119 * @tc.name: DoBidiProcess1
120 * @tc.desc: Verify the DoBidiProcess
121 * @tc.type:FUNC
122 * @tc.require: issueI6V6J4
123 */
124 HWTEST_F(BidiProcesserTest, DoBidiProcess1, TestSize.Level1)
125 {
126 CharGroups cgs = CharGroups::CreateEmpty();
127
128 try {
129 bp.DoBidiProcess(cgs, TextDirection::LTR);
130 } catch(const TexgineException &err) {
131 ASSERT_EQ(ExceptionType::INVALID_ARGUMENT, err.code);
132 }
133 }
134
135 /**
136 * @tc.name: DoBidiProcess2
137 * @tc.desc: Verify the DoBidiProcess
138 * @tc.type:FUNC
139 * @tc.require: issueI6V6J4
140 */
141 HWTEST_F(BidiProcesserTest, DoBidiProcess2, TestSize.Level1)
142 {
143 CharGroups cgs = {};
144
145 try {
146 bp.DoBidiProcess(cgs, TextDirection::LTR);
147 } catch(const TexgineException &err) {
148 ASSERT_EQ(ExceptionType::INVALID_ARGUMENT, err.code);
149 }
150 }
151
152 /**
153 * @tc.name: DoBidiProcess3
154 * @tc.desc: Verify the DoBidiProcess
155 * @tc.type:FUNC
156 * @tc.require: issueI6V6J4
157 */
158 HWTEST_F(BidiProcesserTest, DoBidiProcess3, TestSize.Level1)
159 {
160 InitMyMockVars({.bidi = nullptr});
161
162 try {
163 bp.DoBidiProcess(cgs1_, TextDirection::LTR);
164 } catch(const TexgineException &err) {
165 ASSERT_EQ(ExceptionType::API_FAILED, err.code);
166 }
167 }
168
169 /**
170 * @tc.name: DoBidiProcess4
171 * @tc.desc: Verify the DoBidiProcess
172 * @tc.type:FUNC
173 * @tc.require: issueI6V6J4
174 */
175 HWTEST_F(BidiProcesserTest, DoBidiProcess4, TestSize.Level1)
176 {
177 InitMyMockVars({.status = U_ILLEGAL_ARGUMENT_ERROR});
178
179 try {
180 bp.DoBidiProcess(cgs1_, TextDirection::LTR);
181 } catch(const TexgineException &err) {
182 ASSERT_EQ(ExceptionType::API_FAILED, err.code);
183 }
184 }
185
186 /**
187 * @tc.name: DoBidiProcess5
188 * @tc.desc: Verify the DoBidiProcess
189 * @tc.type:FUNC
190 * @tc.require: issueI6V6J4
191 */
192 HWTEST_F(BidiProcesserTest, DoBidiProcess5, TestSize.Level1)
193 {
194 // set ubidi_countRuns return 0
195 InitMyMockVars({.size = 0,});
196
197 EXPECT_NO_THROW({
198 auto ret = bp.DoBidiProcess(cgs1_, TextDirection::LTR);
199 ASSERT_EQ(ret.size(), 0);
200 });
201 }
202
203 /**
204 * @tc.name: DoBidiProcess6
205 * @tc.desc: Verify the DoBidiProcess
206 * @tc.type:FUNC
207 * @tc.require: issueI6V6J4
208 */
209 HWTEST_F(BidiProcesserTest, DoBidiProcess6, TestSize.Level1)
210 {
211 // -1, 1: Set the output parameter of ubidi_getVisualRun
212 InitMyMockVars({.start = {-1}, .length = {1},});
213
214 try {
215 bp.DoBidiProcess(cgs1_, TextDirection::LTR);
216 } catch(const TexgineException &err) {
217 ASSERT_EQ(ExceptionType::API_FAILED, err.code);
218 }
219 }
220
221 /**
222 * @tc.name: DoBidiProcess7
223 * @tc.desc: Verify the DoBidiProcess
224 * @tc.type:FUNC
225 * @tc.require: issueI6V6J4
226 */
227 HWTEST_F(BidiProcesserTest, DoBidiProcess7, TestSize.Level1)
228 {
229 InitMyMockVars({.start = {1}, .length = {-1},});
230
231 try {
232 bp.DoBidiProcess(cgs1_, TextDirection::LTR);
233 } catch(const TexgineException &err) {
234 ASSERT_EQ(ExceptionType::API_FAILED, err.code);
235 }
236 }
237
238 /**
239 * @tc.name: DoBidiProcess8
240 * @tc.desc: Verify the DoBidiProcess
241 * @tc.type:FUNC
242 * @tc.require: issueI6V6J4
243 */
244 HWTEST_F(BidiProcesserTest, DoBidiProcess8, TestSize.Level1)
245 {
246 InitMyMockVars({.size = 4, .start = {0, 1, 2, 3}, .length = {1, 1, 1, 1}});
247 auto c = cgs1_.GetSub(0, 1);
248 EXPECT_NO_THROW({
249 auto ret = bp.DoBidiProcess(c, TextDirection::LTR);
250 ASSERT_EQ(ret.size(), 1);
251 });
252 }
253
254 /**
255 * @tc.name: DoBidiProcess9
256 * @tc.desc: Verify the DoBidiProcess
257 * @tc.type:FUNC
258 * @tc.require: issueI6V6J4
259 */
260 HWTEST_F(BidiProcesserTest, DoBidiProcess9, TestSize.Level1)
261 {
262 InitMyMockVars({});
263
264 EXPECT_NO_THROW({
265 auto ret = bp.DoBidiProcess(cgs1_, TextDirection::LTR);
266 ASSERT_EQ(ret.size(), 1);
267 ASSERT_EQ(ret[0].rtl, true);
268 });
269 }
270
271 /**
272 * @tc.name: DoBidiProcess10
273 * @tc.desc: Verify the DoBidiProcess
274 * @tc.type:FUNC
275 * @tc.require: issueI6V6J4
276 */
277 HWTEST_F(BidiProcesserTest, DoBidiProcess10, TestSize.Level1)
278 {
279 InitMyMockVars({.rtl = UBIDI_LTR,});
280
281 EXPECT_NO_THROW({
282 auto ret = bp.DoBidiProcess(cgs1_, TextDirection::LTR);
283 ASSERT_EQ(ret.size(), 1);
284 ASSERT_EQ(ret[0].rtl, false);
285 });
286 }
287
288 /**
289 * @tc.name: ProcessBidiText1
290 * @tc.desc: Verify the ProcessBidiText
291 * @tc.type:FUNC
292 * @tc.require: issueI6V6J4
293 */
294 HWTEST_F(BidiProcesserTest, ProcessBidiText1, TestSize.Level1)
295 {
296 InitMyMockVars({});
297 spans_ = {std::make_shared<MockAnySpan>(), TextSpan::MakeFromCharGroups(cgs1_), std::make_shared<MockAnySpan>()};
298
299 EXPECT_NO_THROW({
300 auto ret = bp.ProcessBidiText(spans_, TextDirection::LTR);
301 ASSERT_EQ(ret.size(), 3);
302 ASSERT_NE(ret[0].TryToAnySpan(), nullptr);
303 ASSERT_NE(ret[1].TryToTextSpan(), nullptr);
304 ASSERT_NE(ret[2].TryToAnySpan(), nullptr);
305 });
306 }
307
308 /**
309 * @tc.name: ProcessBidiText2
310 * @tc.desc: Verify the ProcessBidiText
311 * @tc.type:FUNC
312 * @tc.require: issueI6V6J4
313 */
314 HWTEST_F(BidiProcesserTest, ProcessBidiText2, TestSize.Level1)
315 {
316 EXPECT_NO_THROW({
317 auto ret = bp.ProcessBidiText(spans_, TextDirection::LTR);
318 ASSERT_EQ(ret.size(), 0);
319 });
320 }
321
322 /**
323 * @tc.name: ProcessBidiText3
324 * @tc.desc: Verify the ProcessBidiText
325 * @tc.type:FUNC
326 * @tc.require: issueI6V6J4
327 */
328 HWTEST_F(BidiProcesserTest, ProcessBidiText3, TestSize.Level1)
329 {
330 InitMyMockVars({.size = 4, .start = {0, 1, 2, 3}, .length = {1, 1, 1, 1}});
331 auto c = cgs1_.GetSub(0, 1);
332 spans_ = {std::make_shared<MockAnySpan>(), TextSpan::MakeFromCharGroups(c), std::make_shared<MockAnySpan>()};
333
334 EXPECT_NO_THROW({
335 auto ret = bp.ProcessBidiText(spans_, TextDirection::LTR);
336 ASSERT_EQ(ret.size(), 3);
337 ASSERT_NE(ret[0].TryToAnySpan(), nullptr);
338 auto span1 = ret[1].TryToTextSpan();
339 ASSERT_NE(span1, nullptr);
340 ASSERT_EQ(span1->IsRTL(), true);
341 ASSERT_NE(ret[2].TryToAnySpan(), nullptr);
342 });
343 }
344 } // namespace TextEngine
345 } // namespace Rosen
346 } // namespace OHOS
347