• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "unicode/unum.h"
14 
15 
16 class FormattedStringBuilderTest : public IntlTest {
17   public:
18     void testInsertAppendUnicodeString();
19     void testSplice();
20     void testInsertAppendCodePoint();
21     void testCopy();
22     void testFields();
23     void testUnlimitedCapacity();
24     void testCodePoints();
25 
26     void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0);
27 
28   private:
29     void assertEqualsImpl(const UnicodeString &a, const FormattedStringBuilder &b);
30 };
31 
32 static const char16_t *EXAMPLE_STRINGS[] = {
33         u"",
34         u"xyz",
35         u"The quick brown fox jumps over the lazy dog",
36         u"��",
37         u"mixed �� and ASCII",
38         u"with combining characters like ��������",
39         u"A very very very very very very very very very very long string to force heap"};
40 
runIndexedTest(int32_t index,UBool exec,const char * & name,char *)41 void FormattedStringBuilderTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char *) {
42     if (exec) {
43         logln("TestSuite FormattedStringBuilderTest: ");
44     }
45     TESTCASE_AUTO_BEGIN;
46         TESTCASE_AUTO(testInsertAppendUnicodeString);
47         TESTCASE_AUTO(testSplice);
48         TESTCASE_AUTO(testInsertAppendCodePoint);
49         TESTCASE_AUTO(testCopy);
50         TESTCASE_AUTO(testFields);
51         TESTCASE_AUTO(testUnlimitedCapacity);
52         TESTCASE_AUTO(testCodePoints);
53     TESTCASE_AUTO_END;
54 }
55 
testInsertAppendUnicodeString()56 void FormattedStringBuilderTest::testInsertAppendUnicodeString() {
57     UErrorCode status = U_ZERO_ERROR;
58     UnicodeString sb1;
59     FormattedStringBuilder sb2;
60     for (const char16_t* strPtr : EXAMPLE_STRINGS) {
61         UnicodeString str(strPtr);
62 
63         FormattedStringBuilder sb3;
64         sb1.append(str);
65         sb2.append(str, kUndefinedField, status);
66         assertSuccess("Appending to sb2", status);
67         sb3.append(str, kUndefinedField, 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", kUndefinedField, status);
78         assertSuccess("Appending to sb5", status);
79         sb5.insert(2, str, kUndefinedField, 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, kUndefinedField, 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 
testSplice()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, kUndefinedField, status);
128             sb2.splice(cas.startThis, cas.endThis, replacement, 0, replacement.length(), kUndefinedField, 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, kUndefinedField, status);
141             sb2.splice(cas.startThis, cas.endThis, replacement, 1, 3, kUndefinedField, status);
142             assertSuccess("Splicing into sb2 second time", status);
143             assertEqualsImpl(sb1, sb2);
144         }
145     }
146 }
147 
testInsertAppendCodePoint()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, kUndefinedField, status);
158         assertSuccess("Appending to sb2", status);
159         sb3.appendCodePoint(cas, kUndefinedField, 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", kUndefinedField, status);
174         assertSuccess("Appending to sb5", status);
175         sb5.insertCodePoint(2, cas, kUndefinedField, 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), kUndefinedField, status);
184             sb7.insertChar16(0, U16_LEAD(cas), kUndefinedField, status);
185         } else {
186             sb7.insertChar16(0, cas, kUndefinedField, status);
187         }
188         assertSuccess("Insert/append into sb7", status);
189         assertEqualsImpl(sb6, sb7);
190     }
191 }
192 
testCopy()193 void FormattedStringBuilderTest::testCopy() {
194     UErrorCode status = U_ZERO_ERROR;
195     for (UnicodeString str : EXAMPLE_STRINGS) {
196         FormattedStringBuilder sb1;
197         sb1.append(str, kUndefinedField, 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", kUndefinedField, status);
203         assertSuccess("Appending to sb1 second time", status);
204         assertFalse("Content should no longer equal itself", sb1.contentEquals(sb2));
205     }
206 }
207 
testFields()208 void FormattedStringBuilderTest::testFields() {
209     typedef FormattedStringBuilder::Field Field;
210     UErrorCode status = U_ZERO_ERROR;
211     // Note: This is a C++11 for loop that calls the UnicodeString constructor on each iteration.
212     for (UnicodeString str : EXAMPLE_STRINGS) {
213         FormattedValueStringBuilderImpl sbi(kUndefinedField);
214         FormattedStringBuilder& sb = sbi.getStringRef();
215         sb.append(str, kUndefinedField, status);
216         assertSuccess("Appending to sb", status);
217         sb.append(str, {UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD}, status);
218         assertSuccess("Appending to sb", status);
219         assertEquals("Reference string copied twice", str.length() * 2, sb.length());
220         for (int32_t i = 0; i < str.length(); i++) {
221             assertEquals("Null field first",
222                 kUndefinedField.bits, sb.fieldAt(i).bits);
223             assertEquals("Currency field second",
224                 Field(UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD).bits,
225                 sb.fieldAt(i + str.length()).bits);
226         }
227 
228         // Very basic FieldPosition test. More robust tests happen in NumberFormatTest.
229         // Let NumberFormatTest also take care of FieldPositionIterator material.
230         FieldPosition fp(UNUM_CURRENCY_FIELD);
231         sbi.nextFieldPosition(fp, status);
232         assertSuccess("Populating the FieldPosition", status);
233         assertEquals("Currency start position", str.length(), fp.getBeginIndex());
234         assertEquals("Currency end position", str.length() * 2, fp.getEndIndex());
235 
236         if (str.length() > 0) {
237             sb.insertCodePoint(2, 100, {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD}, status);
238             assertSuccess("Inserting code point into sb", status);
239             assertEquals("New length", str.length() * 2 + 1, sb.length());
240             assertEquals("Integer field",
241                 Field(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD).bits,
242                 sb.fieldAt(2).bits);
243         }
244 
245         FormattedStringBuilder old(sb);
246         sb.append(old, status);
247         assertSuccess("Appending to myself", status);
248         int32_t numNull = 0;
249         int32_t numCurr = 0;
250         int32_t numInt = 0;
251         for (int32_t i = 0; i < sb.length(); i++) {
252             auto field = sb.fieldAt(i);
253             assertEquals("Field should equal location in old",
254                 old.fieldAt(i % old.length()).bits, field.bits);
255             if (field == kUndefinedField) {
256                 numNull++;
257             } else if (field == Field(UFIELD_CATEGORY_NUMBER, UNUM_CURRENCY_FIELD)) {
258                 numCurr++;
259             } else if (field == Field(UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD)) {
260                 numInt++;
261             } else {
262                 errln("Encountered unknown field");
263             }
264         }
265         assertEquals("Number of null fields", str.length() * 2, numNull);
266         assertEquals("Number of currency fields", numNull, numCurr);
267         assertEquals("Number of integer fields", str.length() > 0 ? 2 : 0, numInt);
268     }
269 }
270 
testUnlimitedCapacity()271 void FormattedStringBuilderTest::testUnlimitedCapacity() {
272     UErrorCode status = U_ZERO_ERROR;
273     FormattedStringBuilder builder;
274     // The builder should never fail upon repeated appends.
275     for (int i = 0; i < 1000; i++) {
276         UnicodeString message("Iteration #");
277         message += Int64ToUnicodeString(i);
278         assertEquals(message, builder.length(), i);
279         builder.appendCodePoint(u'x', kUndefinedField, status);
280         assertSuccess(message, status);
281         assertEquals(message, builder.length(), i + 1);
282     }
283 }
284 
testCodePoints()285 void FormattedStringBuilderTest::testCodePoints() {
286     UErrorCode status = U_ZERO_ERROR;
287     FormattedStringBuilder nsb;
288     assertEquals("First is -1 on empty string", -1, nsb.getFirstCodePoint());
289     assertEquals("Last is -1 on empty string", -1, nsb.getLastCodePoint());
290     assertEquals("Length is 0 on empty string", 0, nsb.codePointCount());
291 
292     nsb.append(u"q", kUndefinedField, status);
293     assertSuccess("Spot 1", status);
294     assertEquals("First is q", u'q', nsb.getFirstCodePoint());
295     assertEquals("Last is q", u'q', nsb.getLastCodePoint());
296     assertEquals("0th is q", u'q', nsb.codePointAt(0));
297     assertEquals("Before 1st is q", u'q', nsb.codePointBefore(1));
298     assertEquals("Code point count is 1", 1, nsb.codePointCount());
299 
300     // �� is two char16s
301     nsb.append(u"��", kUndefinedField, status);
302     assertSuccess("Spot 2" ,status);
303     assertEquals("First is still q", u'q', nsb.getFirstCodePoint());
304     assertEquals("Last is space ship", 128640, nsb.getLastCodePoint());
305     assertEquals("1st is space ship", 128640, nsb.codePointAt(1));
306     assertEquals("Before 1st is q", u'q', nsb.codePointBefore(1));
307     assertEquals("Before 3rd is space ship", 128640, nsb.codePointBefore(3));
308     assertEquals("Code point count is 2", 2, nsb.codePointCount());
309 }
310 
assertEqualsImpl(const UnicodeString & a,const FormattedStringBuilder & b)311 void FormattedStringBuilderTest::assertEqualsImpl(const UnicodeString &a, const FormattedStringBuilder &b) {
312     // TODO: Why won't this compile without the IntlTest:: qualifier?
313     IntlTest::assertEquals("Lengths should be the same", a.length(), b.length());
314     IntlTest::assertEquals("Code point counts should be the same", a.countChar32(), b.codePointCount());
315 
316     if (a.length() != b.length()) {
317         return;
318     }
319 
320     for (int32_t i = 0; i < a.length(); i++) {
321         IntlTest::assertEquals(
322                 UnicodeString(u"Char at position ") + Int64ToUnicodeString(i) +
323                 UnicodeString(u" in \"") + a + UnicodeString("\" versus \"") +
324                 b.toUnicodeString() + UnicodeString("\""), a.charAt(i), b.charAt(i));
325     }
326 }
327 
328 
createFormattedStringBuilderTest()329 extern IntlTest *createFormattedStringBuilderTest() {
330     return new FormattedStringBuilderTest();
331 }
332 
333 #endif /* #if !UCONFIG_NO_FORMATTING */
334