]> git.saurik.com Git - apple/icu.git/blob - icuSources/test/intltest/formatted_string_builder_test.cpp
ICU-66108.tar.gz
[apple/icu.git] / icuSources / test / intltest / formatted_string_builder_test.cpp
1 // © 2017 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3
4 #include "unicode/utypes.h"
5
6 #if !UCONFIG_NO_FORMATTING
7
8 #include "unicode/utf16.h"
9 #include "putilimp.h"
10 #include "intltest.h"
11 #include "formatted_string_builder.h"
12 #include "formattedval_impl.h"
13
14
15 class FormattedStringBuilderTest : public IntlTest {
16 public:
17 void testInsertAppendUnicodeString();
18 void testSplice();
19 void testInsertAppendCodePoint();
20 void testCopy();
21 void testFields();
22 void testUnlimitedCapacity();
23 void testCodePoints();
24
25 void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0);
26
27 private:
28 void assertEqualsImpl(const UnicodeString &a, const FormattedStringBuilder &b);
29 };
30
31 static const char16_t *EXAMPLE_STRINGS[] = {
32 u"",
33 u"xyz",
34 u"The quick brown fox jumps over the lazy dog",
35 u"😁",
36 u"mixed 😇 and ASCII",
37 u"with combining characters like 🇦🇧🇨🇩",
38 u"A very very very very very very very very very very long string to force heap"};
39
40 void FormattedStringBuilderTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char *) {
41 if (exec) {
42 logln("TestSuite FormattedStringBuilderTest: ");
43 }
44 TESTCASE_AUTO_BEGIN;
45 TESTCASE_AUTO(testInsertAppendUnicodeString);
46 TESTCASE_AUTO(testSplice);
47 TESTCASE_AUTO(testInsertAppendCodePoint);
48 TESTCASE_AUTO(testCopy);
49 TESTCASE_AUTO(testFields);
50 TESTCASE_AUTO(testUnlimitedCapacity);
51 TESTCASE_AUTO(testCodePoints);
52 TESTCASE_AUTO_END;
53 }
54
55 void FormattedStringBuilderTest::testInsertAppendUnicodeString() {
56 UErrorCode status = U_ZERO_ERROR;
57 UnicodeString sb1;
58 FormattedStringBuilder sb2;
59 for (const char16_t* strPtr : EXAMPLE_STRINGS) {
60 UnicodeString str(strPtr);
61
62 FormattedStringBuilder sb3;
63 sb1.append(str);
64 // Note: UNUM_FIELD_COUNT is like passing null in Java
65 sb2.append(str, UNUM_FIELD_COUNT, status);
66 assertSuccess("Appending to sb2", status);
67 sb3.append(str, UNUM_FIELD_COUNT, status);
68 assertSuccess("Appending to sb3", status);
69 assertEqualsImpl(sb1, sb2);
70 assertEqualsImpl(str, sb3);
71
72 UnicodeString sb4;
73 FormattedStringBuilder sb5;
74 sb4.append(u"😇");
75 sb4.append(str);
76 sb4.append(u"xx");
77 sb5.append(u"😇xx", UNUM_FIELD_COUNT, status);
78 assertSuccess("Appending to sb5", status);
79 sb5.insert(2, str, UNUM_FIELD_COUNT, status);
80 assertSuccess("Inserting into sb5", status);
81 assertEqualsImpl(sb4, sb5);
82
83 int start = uprv_min(1, str.length());
84 int end = uprv_min(10, str.length());
85 sb4.insert(3, str, start, end - start); // UnicodeString uses length instead of end index
86 sb5.insert(3, str, start, end, UNUM_FIELD_COUNT, status);
87 assertSuccess("Inserting into sb5 again", status);
88 assertEqualsImpl(sb4, sb5);
89
90 UnicodeString sb4cp(sb4);
91 FormattedStringBuilder sb5cp(sb5);
92 sb4.append(sb4cp);
93 sb5.append(sb5cp, status);
94 assertSuccess("Appending again to sb5", status);
95 assertEqualsImpl(sb4, sb5);
96 }
97 }
98
99 void FormattedStringBuilderTest::testSplice() {
100 static const struct TestCase {
101 const char16_t* input;
102 const int32_t startThis;
103 const int32_t endThis;
104 } cases[] = {
105 { u"", 0, 0 },
106 { u"abc", 0, 0 },
107 { u"abc", 1, 1 },
108 { u"abc", 1, 2 },
109 { u"abc", 0, 2 },
110 { u"abc", 0, 3 },
111 { u"lorem ipsum dolor sit amet", 8, 8 },
112 { u"lorem ipsum dolor sit amet", 8, 11 }, // 3 chars, equal to replacement "xyz"
113 { u"lorem ipsum dolor sit amet", 8, 18 } }; // 10 chars, larger than several replacements
114
115 UErrorCode status = U_ZERO_ERROR;
116 UnicodeString sb1;
117 FormattedStringBuilder sb2;
118 for (auto cas : cases) {
119 for (const char16_t* replacementPtr : EXAMPLE_STRINGS) {
120 UnicodeString replacement(replacementPtr);
121
122 // Test replacement with full string
123 sb1.remove();
124 sb1.append(cas.input);
125 sb1.replace(cas.startThis, cas.endThis - cas.startThis, replacement);
126 sb2.clear();
127 sb2.append(cas.input, UNUM_FIELD_COUNT, status);
128 sb2.splice(cas.startThis, cas.endThis, replacement, 0, replacement.length(), UNUM_FIELD_COUNT, status);
129 assertSuccess("Splicing into sb2 first time", status);
130 assertEqualsImpl(sb1, sb2);
131
132 // Test replacement with partial string
133 if (replacement.length() <= 2) {
134 continue;
135 }
136 sb1.remove();
137 sb1.append(cas.input);
138 sb1.replace(cas.startThis, cas.endThis - cas.startThis, UnicodeString(replacement, 1, 2));
139 sb2.clear();
140 sb2.append(cas.input, UNUM_FIELD_COUNT, status);
141 sb2.splice(cas.startThis, cas.endThis, replacement, 1, 3, UNUM_FIELD_COUNT, status);
142 assertSuccess("Splicing into sb2 second time", status);
143 assertEqualsImpl(sb1, sb2);
144 }
145 }
146 }
147
148 void FormattedStringBuilderTest::testInsertAppendCodePoint() {
149 static const UChar32 cases[] = {
150 0, 1, 60, 127, 128, 0x7fff, 0x8000, 0xffff, 0x10000, 0x1f000, 0x10ffff};
151 UErrorCode status = U_ZERO_ERROR;
152 UnicodeString sb1;
153 FormattedStringBuilder sb2;
154 for (UChar32 cas : cases) {
155 FormattedStringBuilder sb3;
156 sb1.append(cas);
157 sb2.appendCodePoint(cas, UNUM_FIELD_COUNT, status);
158 assertSuccess("Appending to sb2", status);
159 sb3.appendCodePoint(cas, UNUM_FIELD_COUNT, status);
160 assertSuccess("Appending to sb3", status);
161 assertEqualsImpl(sb1, sb2);
162 assertEquals("Length of sb3", U16_LENGTH(cas), sb3.length());
163 assertEquals("Code point count of sb3", 1, sb3.codePointCount());
164 assertEquals(
165 "First code unit in sb3",
166 !U_IS_SUPPLEMENTARY(cas) ? (char16_t) cas : U16_LEAD(cas),
167 sb3.charAt(0));
168
169 UnicodeString sb4;
170 FormattedStringBuilder sb5;
171 sb4.append(u"😇xx");
172 sb4.insert(2, cas);
173 sb5.append(u"😇xx", UNUM_FIELD_COUNT, status);
174 assertSuccess("Appending to sb5", status);
175 sb5.insertCodePoint(2, cas, UNUM_FIELD_COUNT, status);
176 assertSuccess("Inserting into sb5", status);
177 assertEqualsImpl(sb4, sb5);
178
179 UnicodeString sb6;
180 FormattedStringBuilder sb7;
181 sb6.append(cas);
182 if (U_IS_SUPPLEMENTARY(cas)) {
183 sb7.appendChar16(U16_TRAIL(cas), UNUM_FIELD_COUNT, status);
184 sb7.insertChar16(0, U16_LEAD(cas), UNUM_FIELD_COUNT, status);
185 } else {
186 sb7.insertChar16(0, cas, UNUM_FIELD_COUNT, status);
187 }
188 assertSuccess("Insert/append into sb7", status);
189 assertEqualsImpl(sb6, sb7);
190 }
191 }
192
193 void FormattedStringBuilderTest::testCopy() {
194 UErrorCode status = U_ZERO_ERROR;
195 for (UnicodeString str : EXAMPLE_STRINGS) {
196 FormattedStringBuilder sb1;
197 sb1.append(str, UNUM_FIELD_COUNT, status);
198 assertSuccess("Appending to sb1 first time", status);
199 FormattedStringBuilder sb2(sb1);
200 assertTrue("Content should equal itself", sb1.contentEquals(sb2));
201
202 sb1.append("12345", UNUM_FIELD_COUNT, status);
203 assertSuccess("Appending to sb1 second time", status);
204 assertFalse("Content should no longer equal itself", sb1.contentEquals(sb2));
205 }
206 }
207
208 void FormattedStringBuilderTest::testFields() {
209 UErrorCode status = U_ZERO_ERROR;
210 // Note: This is a C++11 for loop that calls the UnicodeString constructor on each iteration.
211 for (UnicodeString str : EXAMPLE_STRINGS) {
212 FormattedValueStringBuilderImpl sbi(0);
213 FormattedStringBuilder& sb = sbi.getStringRef();
214 sb.append(str, UNUM_FIELD_COUNT, status);
215 assertSuccess("Appending to sb", status);
216 sb.append(str, UNUM_CURRENCY_FIELD, status);
217 assertSuccess("Appending to sb", status);
218 assertEquals("Reference string copied twice", str.length() * 2, sb.length());
219 for (int32_t i = 0; i < str.length(); i++) {
220 assertEquals("Null field first",
221 (FormattedStringBuilder::Field) UNUM_FIELD_COUNT, sb.fieldAt(i));
222 assertEquals("Currency field second",
223 (FormattedStringBuilder::Field) UNUM_CURRENCY_FIELD, sb.fieldAt(i + str.length()));
224 }
225
226 // Very basic FieldPosition test. More robust tests happen in NumberFormatTest.
227 // Let NumberFormatTest also take care of FieldPositionIterator material.
228 FieldPosition fp(UNUM_CURRENCY_FIELD);
229 sbi.nextFieldPosition(fp, status);
230 assertSuccess("Populating the FieldPosition", status);
231 assertEquals("Currency start position", str.length(), fp.getBeginIndex());
232 assertEquals("Currency end position", str.length() * 2, fp.getEndIndex());
233
234 if (str.length() > 0) {
235 sb.insertCodePoint(2, 100, UNUM_INTEGER_FIELD, status);
236 assertSuccess("Inserting code point into sb", status);
237 assertEquals("New length", str.length() * 2 + 1, sb.length());
238 assertEquals("Integer field", (FormattedStringBuilder::Field) UNUM_INTEGER_FIELD, sb.fieldAt(2));
239 }
240
241 FormattedStringBuilder old(sb);
242 sb.append(old, status);
243 assertSuccess("Appending to myself", status);
244 int32_t numNull = 0;
245 int32_t numCurr = 0;
246 int32_t numInt = 0;
247 for (int32_t i = 0; i < sb.length(); i++) {
248 FormattedStringBuilder::Field field = sb.fieldAt(i);
249 assertEquals("Field should equal location in old", old.fieldAt(i % old.length()), field);
250 if (field == UNUM_FIELD_COUNT) {
251 numNull++;
252 } else if (field == UNUM_CURRENCY_FIELD) {
253 numCurr++;
254 } else if (field == UNUM_INTEGER_FIELD) {
255 numInt++;
256 } else {
257 errln("Encountered unknown field");
258 }
259 }
260 assertEquals("Number of null fields", str.length() * 2, numNull);
261 assertEquals("Number of currency fields", numNull, numCurr);
262 assertEquals("Number of integer fields", str.length() > 0 ? 2 : 0, numInt);
263 }
264 }
265
266 void FormattedStringBuilderTest::testUnlimitedCapacity() {
267 UErrorCode status = U_ZERO_ERROR;
268 FormattedStringBuilder builder;
269 // The builder should never fail upon repeated appends.
270 for (int i = 0; i < 1000; i++) {
271 UnicodeString message("Iteration #");
272 message += Int64ToUnicodeString(i);
273 assertEquals(message, builder.length(), i);
274 builder.appendCodePoint(u'x', UNUM_FIELD_COUNT, status);
275 assertSuccess(message, status);
276 assertEquals(message, builder.length(), i + 1);
277 }
278 }
279
280 void FormattedStringBuilderTest::testCodePoints() {
281 UErrorCode status = U_ZERO_ERROR;
282 FormattedStringBuilder nsb;
283 assertEquals("First is -1 on empty string", -1, nsb.getFirstCodePoint());
284 assertEquals("Last is -1 on empty string", -1, nsb.getLastCodePoint());
285 assertEquals("Length is 0 on empty string", 0, nsb.codePointCount());
286
287 nsb.append(u"q", UNUM_FIELD_COUNT, status);
288 assertSuccess("Spot 1", status);
289 assertEquals("First is q", u'q', nsb.getFirstCodePoint());
290 assertEquals("Last is q", u'q', nsb.getLastCodePoint());
291 assertEquals("0th is q", u'q', nsb.codePointAt(0));
292 assertEquals("Before 1st is q", u'q', nsb.codePointBefore(1));
293 assertEquals("Code point count is 1", 1, nsb.codePointCount());
294
295 // 🚀 is two char16s
296 nsb.append(u"🚀", UNUM_FIELD_COUNT, status);
297 assertSuccess("Spot 2" ,status);
298 assertEquals("First is still q", u'q', nsb.getFirstCodePoint());
299 assertEquals("Last is space ship", 128640, nsb.getLastCodePoint());
300 assertEquals("1st is space ship", 128640, nsb.codePointAt(1));
301 assertEquals("Before 1st is q", u'q', nsb.codePointBefore(1));
302 assertEquals("Before 3rd is space ship", 128640, nsb.codePointBefore(3));
303 assertEquals("Code point count is 2", 2, nsb.codePointCount());
304 }
305
306 void FormattedStringBuilderTest::assertEqualsImpl(const UnicodeString &a, const FormattedStringBuilder &b) {
307 // TODO: Why won't this compile without the IntlTest:: qualifier?
308 IntlTest::assertEquals("Lengths should be the same", a.length(), b.length());
309 IntlTest::assertEquals("Code point counts should be the same", a.countChar32(), b.codePointCount());
310
311 if (a.length() != b.length()) {
312 return;
313 }
314
315 for (int32_t i = 0; i < a.length(); i++) {
316 IntlTest::assertEquals(
317 UnicodeString(u"Char at position ") + Int64ToUnicodeString(i) +
318 UnicodeString(u" in \"") + a + UnicodeString("\" versus \"") +
319 b.toUnicodeString() + UnicodeString("\""), a.charAt(i), b.charAt(i));
320 }
321 }
322
323
324 extern IntlTest *createFormattedStringBuilderTest() {
325 return new FormattedStringBuilderTest();
326 }
327
328 #endif /* #if !UCONFIG_NO_FORMATTING */