1 // © 2017 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 #include "unicode/utypes.h"
6 #if !UCONFIG_NO_FORMATTING
8 #include "unicode/utf16.h"
11 #include "formatted_string_builder.h"
12 #include "formattedval_impl.h"
15 class FormattedStringBuilderTest
: public IntlTest
{
17 void testInsertAppendUnicodeString();
19 void testInsertAppendCodePoint();
22 void testUnlimitedCapacity();
23 void testCodePoints();
25 void runIndexedTest(int32_t index
, UBool exec
, const char *&name
, char *par
= 0);
28 void assertEqualsImpl(const UnicodeString
&a
, const FormattedStringBuilder
&b
);
31 static const char16_t *EXAMPLE_STRINGS
[] = {
34 u
"The quick brown fox jumps over the lazy dog",
37 u
"with combining characters like 🇦🇧🇨🇩",
38 u
"A very very very very very very very very very very long string to force heap"};
40 void FormattedStringBuilderTest::runIndexedTest(int32_t index
, UBool exec
, const char *&name
, char *) {
42 logln("TestSuite FormattedStringBuilderTest: ");
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
);
55 void FormattedStringBuilderTest::testInsertAppendUnicodeString() {
56 UErrorCode status
= U_ZERO_ERROR
;
58 FormattedStringBuilder sb2
;
59 for (const char16_t* strPtr
: EXAMPLE_STRINGS
) {
60 UnicodeString
str(strPtr
);
62 FormattedStringBuilder sb3
;
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
);
73 FormattedStringBuilder sb5
;
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
);
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
);
90 UnicodeString
sb4cp(sb4
);
91 FormattedStringBuilder
sb5cp(sb5
);
93 sb5
.append(sb5cp
, status
);
94 assertSuccess("Appending again to sb5", status
);
95 assertEqualsImpl(sb4
, sb5
);
99 void FormattedStringBuilderTest::testSplice() {
100 static const struct TestCase
{
101 const char16_t* input
;
102 const int32_t startThis
;
103 const int32_t endThis
;
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
115 UErrorCode status
= U_ZERO_ERROR
;
117 FormattedStringBuilder sb2
;
118 for (auto cas
: cases
) {
119 for (const char16_t* replacementPtr
: EXAMPLE_STRINGS
) {
120 UnicodeString
replacement(replacementPtr
);
122 // Test replacement with full string
124 sb1
.append(cas
.input
);
125 sb1
.replace(cas
.startThis
, cas
.endThis
- cas
.startThis
, replacement
);
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
);
132 // Test replacement with partial string
133 if (replacement
.length() <= 2) {
137 sb1
.append(cas
.input
);
138 sb1
.replace(cas
.startThis
, cas
.endThis
- cas
.startThis
, UnicodeString(replacement
, 1, 2));
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
);
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
;
153 FormattedStringBuilder sb2
;
154 for (UChar32 cas
: cases
) {
155 FormattedStringBuilder sb3
;
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());
165 "First code unit in sb3",
166 !U_IS_SUPPLEMENTARY(cas
) ? (char16_t) cas
: U16_LEAD(cas
),
170 FormattedStringBuilder sb5
;
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
);
180 FormattedStringBuilder sb7
;
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
);
186 sb7
.insertChar16(0, cas
, UNUM_FIELD_COUNT
, status
);
188 assertSuccess("Insert/append into sb7", status
);
189 assertEqualsImpl(sb6
, sb7
);
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
));
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
));
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()));
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());
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));
241 FormattedStringBuilder
old(sb
);
242 sb
.append(old
, status
);
243 assertSuccess("Appending to myself", status
);
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
) {
252 } else if (field
== UNUM_CURRENCY_FIELD
) {
254 } else if (field
== UNUM_INTEGER_FIELD
) {
257 errln("Encountered unknown field");
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
);
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);
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());
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());
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());
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());
311 if (a
.length() != b
.length()) {
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
));
324 extern IntlTest
*createFormattedStringBuilderTest() {
325 return new FormattedStringBuilderTest();
328 #endif /* #if !UCONFIG_NO_FORMATTING */